# linearstories > Agent-oriented reference for the linearstories CLI. linearstories synchronizes Markdown epics and user stories with Linear while preserving a two-level issue hierarchy. Canonical repository: https://github.com/stackingturtles/linearstories Human documentation: https://github.com/stackingturtles/linearstories/blob/main/README.md Markdown format: https://github.com/stackingturtles/linearstories/blob/main/docs/USER_STORY_FORMAT.md Releases: https://github.com/stackingturtles/linearstories/releases Current documented version: 1.6.0 ## Core model linearstories supports exactly two hierarchy levels: 1. Epic: a top-level Linear issue whose per-issue `labels` contains the exact, case-sensitive label `Epic`. 2. User story: an issue without the `Epic` label. It may be standalone or linked to an epic through `epic` metadata. Epic requirements: - Include the exact `Epic` label in per-issue metadata. - Include a substantive `### Why is this needed?` section. - Do not include `### Acceptance Criteria`. - Do not include `epic` metadata. Nested epics are invalid. User story requirements: - Do not include the `Epic` label. - Include `### Acceptance Criteria` with at least one `- [ ]` checkbox. - Set `epic` to an existing Linear identifier or an exact local epic title when the story has a parent. ## Installation Prefer the installed executable for regular use: ```bash bun install -g linearstories linearstories --version ``` Use `bunx linearstories ...` only for a one-off trial. Release binaries can also be placed on `PATH`. ## Configuration Preferred interactive setup: ```bash linearstories initctx ``` `initctx` prompts for a context name, masked Linear API token, default team, default project, and comma-separated default labels. It edits the effective discovered config or creates `~/.config/linearstories/config.json`, with `0600` file permissions. Use `--config ` to target another file. If a legacy flat config is migrated, it is retained as the `default` context. Update an existing context directly: ```bash linearstories updatectx orgA ``` `updatectx ` preloads the current defaults, preserves the stored token when its masked prompt is left blank, and fails without writing when the context does not exist. It supports `--config `. Delete an existing context with confirmation: ```bash linearstories deletectx orgA ``` `deletectx ` preserves all other contexts and removes the config file when the deleted context was the last one. Cancellation and unknown names leave the file unchanged. It supports `--config `. Inspect contexts without exposing token values: ```bash linearstories contexts linearstories ctx ``` Both listing forms support `--config `. Output reports whether each token is configured but never includes the token itself. Configuration discovery order: 1. `--config ` 2. `.linearrc.json` in the current directory 3. `~/.config/linearstories/config.json` Minimal `.linearrc.json`: ```json { "apiKey": "lin_api_xxxxxxxxxxxxxxxxxxxx", "defaultTeam": "Engineering", "defaultProject": "Q1 Release", "defaultLabels": ["User Story"] } ``` `LINEAR_API_KEY` overrides the configured `apiKey`. Import and export use configuration; import requires an API key even for local `--dry-run`. `visualize` does not load configuration or require an API key. Do not put `Epic` in `defaultLabels`. It is an issue-type discriminator and must appear only on individual epics. Named contexts are supported: ```json { "contexts": [ { "name": "orgA", "apiKey": "lin_api_org_a", "defaultTeam": "Engineering", "defaultProject": "Q1 Release", "defaultLabels": [] } ] } ``` The only named context is selected automatically. When two or more contexts exist, select one with `--context orgA`. ## Target precedence Effective team and project values use this precedence: 1. CLI `--team` or `--project` 2. File YAML frontmatter `team` or `project` 3. Config `defaultTeam` or `defaultProject` The CLI flags are true overrides. Team and project are file-level fields, not per-issue metadata fields. The target team and project must already exist in Linear. linearstories does not create teams or projects. ## Markdown format One file may contain multiple issues. Each issue begins with an H2 heading. Optional file frontmatter sets team and project. A fenced YAML block immediately after each H2 contains issue metadata. ````markdown --- project: "Q1 Release" team: "Engineering" --- ## Account access ```yaml linear_id: linear_url: labels: [Epic, Auth] priority: 2 status: Backlog ``` Provide secure account access and recovery. ### Why is this needed? Users need secure access to protected features. ## As a user, I want to log in so that I can access my account ```yaml linear_id: linear_url: epic: Account access labels: [Feature, Auth] priority: 2 estimate: 3 assignee: jane@example.com status: Backlog ``` Allow registered users to authenticate. ### Acceptance Criteria - [ ] Valid credentials create an authenticated session - [ ] Invalid credentials return a clear error ```` Supported issue metadata: - `linear_id`: existing Linear identifier, for example `ENG-42`. Empty means create. - `linear_url`: Linear URL. Written after creation. - `epic`: parent Linear identifier or exact title of a local epic in the same import invocation. - `priority`: `0` None, `1` Urgent, `2` High, `3` Normal, `4` Low. - `labels`: exact Linear label names. - `estimate`: story points. - `assignee`: workspace member email or display name. - `status`: workflow state name for the effective team. `linear_id` present means update. Empty or absent means create. Successful creates write `linear_id` and `linear_url` back to the source Markdown unless `--no-write-back` is used. ## Project Atlas visualization Render one Markdown file in Project Atlas, a local interactive project, epic, story, and acceptance-criteria graph: ```bash linearstories visualize stories/project.md ``` Behavior: - Parses exactly one file with the canonical LinearStories parser. - Displays epic-linked stories beneath their epic and standalone stories beneath the project. - Displays an unresolved Linear-style parent identifier, such as `ENG-42`, as a read-only placeholder epic because visualization does not query Linear. - Generates deterministic local display IDs for issues without `linear_id`. - Provides `Collapse`, `Expand all`, and `Fit view` graph controls. - Creates category-filter buttons from every user-story label except exact `Epic`; lowercase `epic` remains a user-story category because issue typing is case-sensitive. - Hides matching stories when a category is disabled without changing the complete-file totals shown in the header. - Shows title, progress, acceptance criteria, labels, and `linear_url` in the selected issue's details panel. - Maps `Done` and `Completed` to completed, `In Progress` and `In-Progress` to in progress, and uses checked acceptance criteria as an in-progress fallback. Other workflow states display as not started. - Fails before server startup when a local epic reference is missing or ambiguous. - Binds only to `127.0.0.1`; default port is `4173`. - Opens the default browser unless `--no-open` is passed. - Does not read config, call Linear, or modify the source file. - Serves issue data locally. The browser retrieves D3 and web fonts from public CDNs. ```text linearstories visualize [options] -p, --port Local HTTP server port; default 4173 --no-open Start the server without opening a browser ``` Stop the server with `Ctrl+C`. ## Safe import workflow Recommended agent sequence: ```bash # 1. Validate Markdown and local hierarchy without Linear API calls linearstories import --dry-run stories/*.md # 2. Validate all remote prerequisites without creating labels or issues linearstories import --preflight stories/*.md # 3. Import after preflight passes linearstories import stories/*.md ``` A normal import always repeats remote preflight before issue mutation, so step 2 is optional but useful for explicit review. Remote preflight validates the complete input set before any issue is created or updated: - Teams - Projects accessible to each team - Team-scoped and workspace-scoped labels - Label casing and label-group exclusivity - Assignees - Team workflow states - Existing parent epic references - API access needed for these reads If local validation, remote preflight, or authorized label provisioning fails, no issue mutation begins. After preflight passes and issue mutation starts, an API failure can leave earlier issue mutations in place. Fix the failure and rerun; populated `linear_id` values make successful prior creations update instead of duplicate. Epics are mutated before user stories. Local parent title references are resolved across every file passed to the same command. Source result order is preserved. ## Label rules Labels are deduplicated and checked once per effective team. Resolution order: 1. Exact label scoped to the target team 2. Exact workspace-scoped label Never select a same-named label owned exclusively by another team. Default behavior is strict: - Missing ordinary labels fail preflight. - Missing exact `Epic` fails preflight. - Case-only applicable labels fail preflight. - Multiple requested child labels from the same Linear label group fail preflight. - Labels are never created implicitly. To create unresolved labels explicitly: ```bash linearstories import --create-missing-labels stories/*.md ``` This creates team-scoped labels, including `Epic`, only after all other remote prerequisites pass. Creation is idempotent on rerun. A creation failure prevents issue mutation. Applicable case-only collisions are not auto-created because they are ambiguous. To explicitly continue without unavailable labels: ```bash linearstories import --allow-missing-labels stories/*.md ``` Skipped ordinary labels appear in the preflight report. The reserved `Epic` label is never skippable because it defines the issue type. For an update containing skipped labels, linearstories omits label synchronization so existing Linear labels are not cleared. Case-only collisions applicable to the target team/workspace and label-group conflicts remain hard failures. ## Import flags ```text linearstories import [options] -c, --config Config file path --context Named config context -t, --team Override file/config team -p, --project Override file/config project --dry-run Local validation only; no Linear API calls --preflight Read-only remote validation; no labels or issues created --create-missing-labels Provision unresolved team labels, then import --allow-missing-labels Explicitly skip unavailable labels --no-write-back Do not write created IDs and URLs into Markdown ``` Invalid combinations: - `--dry-run` with `--preflight` - `--dry-run` with either remote label option - `--preflight` with `--create-missing-labels` - `--create-missing-labels` with `--allow-missing-labels` The command exits nonzero when validation or import results contain failures. ## Context commands ```text linearstories initctx [--config ] linearstories updatectx [--config ] linearstories deletectx [--config ] linearstories contexts [--config ] linearstories ctx [--config ] ``` - `initctx` is interactive and intended for human setup. Agents and automation may write the documented JSON shape directly. - `updatectx ` updates only an existing context and never creates the requested name. - `deletectx ` requires confirmation and removes the config file after its final context. - Reusing a context name requires confirmation before replacement. - Leaving the token prompt blank while updating preserves the stored token. - `Epic` is rejected as a default label because it is the issue-type discriminator. - `contexts` and `ctx` never emit stored API-token values. ## Export Export Linear issues into re-importable Markdown: ```bash linearstories export -t "Engineering" -p "Q1 Release" -o stories/exported.md ``` Useful export filters: ```text -t, --team -p, --project -i, --issues -s, --status -a, --assignee --creator -l, --label --epics-only -o, --output ``` Export only epics in a team and project: ```bash linearstories export \ --team "Engineering" \ --project "Q1 Release" \ --epics-only \ --output stories/epics.md ``` `--epics-only` requires both the exact `Epic` label and no parent issue, and cannot be combined with `--label`. Project filters require a team supplied by `--team` or `defaultTeam`. Team and project names are resolved before issue fetching; resolution failure exits nonzero without an unscoped query and without creating or overwriting the output file. Returned issues are checked against the resolved team, project, label, and top-level requirements before writing. API failures also leave the output untouched. Export relationships are selected inline in one GraphQL request per issue page. Transient `503` page failures use at most three attempts with exponential delay. Do not post-process a workspace-wide export to find epics; use `--epics-only` so Linear applies the project, team, label, and parent filters server-side. Exported epics retain the exact `Epic` label. Exported child issues use the stable parent identifier as `epic` metadata. ## Agent operating rules 1. Read the complete input set before modifying stories. 2. Preserve existing `linear_id` and `linear_url` unless intentionally creating a new issue. 3. Never add `Epic` to `defaultLabels` or to a user story. 4. Never add acceptance criteria to an epic. 5. Never remove acceptance criteria from a user story. 6. Use exact local epic titles or stable Linear identifiers in `epic`. 7. Run `--dry-run`, then `--preflight`, before a large import. 8. Do not use `--allow-missing-labels` unless metadata loss is explicitly accepted. 9. Use `--create-missing-labels` only with authorization to change the target team's taxonomy. 10. Inspect the consolidated preflight report before retrying failures. 11. Use Project Atlas through `linearstories visualize ` when hierarchy, category distribution, or completion needs human visual review; it is read-only and does not need Linear credentials.