linear-sync

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

linear-sync

linear-sync

Move the Linear issues linked to the current branch through their workflow states. This skill is the single source of truth for how issues are transitioned: resolving the live state IDs, extracting issue IDs from a branch name, and the per-state transition rules. Callers decide when and whether to fire it; the mechanics live here once so the rules don't drift across the ship flow, branch cleanup, and the start-of-work transition.
将当前分支关联的Linear issues在其工作流状态间进行转换。本skill是issue状态转换方式的唯一权威来源:包括解析实时状态ID、从分支名称提取issue ID,以及各状态的转换规则。调用方决定何时以及是否触发它;转换机制统一在此实现,确保规则在发布流程、分支清理和工作启动转换等场景中保持一致。

Configuration

配置

Two knobs live in
config.json
beside this file. Read it at the start of a run and use its values throughout. Edit your copied
config.json
to match the consuming repo:
KeyMeaningDefault
linearTeamName
Linear team name used to resolve the live state IDs. Use the name, not the key — the key is renamed over time but the name is stable.
"ACME Skunkworks"
issueKeys
Team-key prefixes that may appear in branch names. The issue-ID regex is built from these.
["A"]
A neutral
config.example.json
ships alongside it as a template — copy it over
config.json
and fill in your values, or edit
config.json
directly.
本文件旁的
config.json
中有两个配置项。运行开始时读取该文件,并在整个流程中使用其值。请编辑你复制的
config.json
以匹配当前仓库的需求:
键名含义默认值
linearTeamName
用于解析实时状态ID的Linear团队名称。请使用名称而非key——key会随重命名而失效,但名称保持稳定。
"ACME Skunkworks"
issueKeys
可能出现在分支名称中的团队key前缀。issue ID的正则表达式将基于这些前缀构建。
["A"]
随工具附带的中立模板
config.example.json
可作为参考——将其复制并重命名为
config.json
后填写你的值,或直接编辑
config.json

Usage modes

使用模式

--dry-run
is passed through
$ARGUMENTS
(the agent reads it), matching the
--dry-run
"preview, change nothing" convention used across the other skills.
Normal — resolve state IDs, extract the branch's issue IDs, apply the transition, and report what moved:
bash
linear-sync
Dry run — resolve state IDs and each issue's current state, report the intended transition (or skip reason) per issue, and exit without any
save_issue
call
:
bash
linear-sync --dry-run
Under
--dry-run
the resolve + read steps still run (they are read-only), so the preview is accurate; only the
save_issue
write in the transition step is skipped. End the report with
DRY RUN — no issues were changed.
--dry-run
参数通过
$ARGUMENTS
传递(由agent读取),遵循其他skill通用的
--dry-run
“预览、不做任何更改”约定。
正常模式——解析状态ID,提取分支的issue ID,执行状态转换,并报告哪些issue发生了状态变更:
bash
linear-sync
Dry Run模式——解析状态ID和每个issue的当前状态,报告每个issue的预期转换(或跳过原因),然后退出不执行任何
save_issue
调用
bash
linear-sync --dry-run
--dry-run
模式下,解析和读取步骤仍会执行(这些是只读操作),因此预览结果准确;仅会跳过转换步骤中的
save_issue
写入操作。报告末尾会显示
DRY RUN — no issues were changed.

Resolving the target state (do this once per run)

解析目标状态(每次运行仅执行一次)

Call
mcp__linear-server__list_issue_statuses
with
team: <linearTeamName>
once to fetch the team's live workflow states. Each carries a stable
type
(
triage
/
backlog
/
unstarted
/
started
/
completed
/
canceled
), a display
name
, an
id
, and a
position
.
Resolve the target by
type
, not by display name.
Display names are customisable — a consumer may rename
In Progress
Doing
or
In Review
Code Review
— so matching the literal name silently fails to find the state (the biggest correctness gap for adopters). Map each transition to a concrete state:
  • In Progress → the
    started
    state named "In Progress" (case-insensitive); else the earliest
    started
    by
    position
    .
  • In Review → the
    started
    state whose name matches "In Review" / "Review"; else, when there are ≥2
    started
    states, the latest
    started
    by
    position
    ; else the In Progress state.
  • Done → the
    completed
    state named "Done"; else the earliest
    completed
    by
    position
    .
started
covers both In Progress and In Review, so the name match (then
position
) is what separates them; a team with a single
started
state resolves both targets to it. Use the resolved
id
in the
save_issue
call.
Pass the team name, not the key. Linear state IDs are per-team, and a workspace's team can be renamed over its lifetime (e.g. CAT → WTF → AKW → ASW), so a hardcoded key goes stale. The team name (
linearTeamName
) does not move. This is the canonical gotcha for adopters — resolve by name, every run.
调用
mcp__linear-server__list_issue_statuses
并传入
team: <linearTeamName>
仅调用一次即可获取团队的实时工作流状态。每个状态包含一个稳定的
type
triage
/
backlog
/
unstarted
/
started
/
completed
/
canceled
)、显示名称
name
、ID
id
和位置
position
通过
type
而非显示名称解析目标状态
。显示名称可自定义——使用者可能会将
In Progress
重命名为
Doing
,或把
In Review
改为
Code Review
,因此匹配字面名称会导致无法找到状态(这是使用者最常遇到的正确性问题)。将每个转换映射到具体状态:
  • In Progress → 类型为
    started
    且名称为"In Progress"的状态(不区分大小写);若不存在,则选择
    position
    最早的
    started
    状态。
  • In Review → 类型为
    started
    且名称匹配"In Review" / "Review"的状态;若不存在,且有≥2个
    started
    状态,则选择
    position
    最晚的
    started
    状态;否则选择In Progress状态。
  • Done → 类型为
    completed
    且名称为"Done"的状态;若不存在,则选择
    position
    最早的
    completed
    状态。
started
类型涵盖In Progress和In Review,因此名称匹配(其次是
position
)是区分两者的依据;若团队只有一个
started
状态,则两个目标状态都会解析为该状态。在
save_issue
调用中使用解析得到的
id
传入团队名称而非key。Linear状态ID是按团队划分的,且工作区中的团队可能会多次重命名(例如CAT → WTF → AKW → ASW),因此硬编码的key会失效。团队名称(
linearTeamName
)是固定不变的。这是使用者最容易犯的错误——每次运行都要通过名称解析。

Extracting issue IDs from the branch

从分支中提取issue ID

Build the issue-ID regex deterministically — mirror the canonical, tested
buildIssueRe
in the repo-root
lib/issue-keys.mjs
, which
pnpm vendor:sync
copies into each consuming bundle (ADR-0004) and which exists for exactly this job:
  1. Escape regex metacharacters in each key (a configured key such as
    C++
    would otherwise throw or silently widen the match).
  2. Group the alternation. Wrap the keys in
    (?:…)
    whenever there is more than one, so the
    -\d+
    binds to the whole alternation:
    \b(?:A|B)-\d+\b
    . The naive join
    \bA|B-\d+\b
    is wrong — it parses as
    \bA
    or
    B-\d+\b
    , matching a bare
    A
    and missing
    A-7
    . A single key needs no wrapper:
    \bA-\d+\b
    .
  3. Guard the empty case. With no configured keys, match nothing — never build an empty alternation (it would match the empty string before every
    -<digits>
    and inject bogus IDs like
    -2
    ).
Match the result (with the
g
flag) against the upper-cased branch name — branches like
asw-7-as-acquired
carry the key in lower case, and a flow such as
--issue=A-7
produces upper-case branch names like
A-7-as-acquired
. Keeping the legacy keys means leftover branches from before a team-key rename are still recognised. Deduplicate the matches. Bogus or malformed IDs simply error on lookup and are skipped with a warning — no separate validation pass.
When a caller already has an
issues
list to hand (e.g. a changelog step emits one), use that instead of re-extracting.
确定性地构建issue ID正则表达式——镜像仓库根目录
lib/issue-keys.mjs
中经过测试的标准
buildIssueRe
函数,该函数会通过
pnpm vendor:sync
复制到每个消费包中(ADR-0004),专门用于完成此任务:
  1. 转义正则元字符——对每个key中的正则元字符进行转义(例如配置的key为
    C++
    时,若不转义会导致错误或匹配范围意外扩大)。
  2. 分组备选分支——当有多个key时,将它们包裹在
    (?:…)
    中,确保
    -\d+
    绑定到整个备选分支:
    \b(?:A|B)-\d+\b
    。直接拼接的
    \bA|B-\d+\b
    错误的——它会解析为
    \bA
    B-\d+\b
    ,匹配单独的
    A
    且无法匹配
    A-7
    。单个key无需包裹:
    \bA-\d+\b
  3. 处理空配置情况——若未配置任何key,则不匹配任何内容——绝不要构建空的备选分支(否则会匹配每个
    -<数字>
    前的空字符串,生成
    -2
    这类无效ID)。
将构建好的正则表达式(带
g
标志)与大写的分支名称进行匹配——分支名称如
asw-7-as-acquired
中的key为小写,而
--issue=A-7
这类流程会生成
A-7-as-acquired
这类大写分支名称。保留旧key意味着团队key重命名前遗留的分支仍能被识别。对匹配结果进行去重。无效或格式错误的ID在查找时会直接报错,并被跳过同时给出警告——无需单独的验证步骤。
若调用方已持有
issues
列表(例如变更日志步骤生成的列表),则直接使用该列表,无需重新提取。

Transition rules

转换规则

For each issue ID, call
mcp__linear-server__get_issue
to read its current state (use its
type
, not its display name). Decide using the progression order of state types:
text
triage < backlog < unstarted < started < completed
canceled
(which also covers a
Duplicate
state) is terminal and sits outside the line. Within
started
, order by
position
, so In Progress precedes In Review. All transitions are idempotent: apply only when the current state is earlier in the progression than the resolved target; skip silently when the issue is already at or past it, or when its current type is
completed
or
canceled
(terminal states are never advanced automatically).
  • In Progress (fired when starting work on an issue) — apply from
    triage
    /
    backlog
    /
    unstarted
    ; skip from any
    started
    ,
    completed
    , or
    canceled
    .
  • In Review (fired on PR open/update inside a ship flow) — apply from
    triage
    /
    backlog
    /
    unstarted
    , or from a
    started
    state earlier by
    position
    than the resolved In Review (e.g. In Progress); skip once at or past In Review, or
    completed
    /
    canceled
    .
  • Done (fired on branch cleanup) — apply from
    triage
    /
    backlog
    /
    unstarted
    /
    started
    ; skip from
    completed
    /
    canceled
    .
Apply a transition with
mcp__linear-server__save_issue
using the resolved state
id
— it is unambiguous across renames. The display-name form
state: "<target>"
works only when the consumer kept Linear's default state names.
Under
--dry-run
, skip this
save_issue
call.
Still read each issue's current state with
get_issue
and decide whether it would transition, but report the intended move (e.g.
A-7: Todo → In Progress (would apply)
/
A-9: In Review (would skip — already at/past target)
) instead of writing it.
Canceled
is the Linear API's own US spelling — keep it as-is when referenced in code or config.
对每个issue ID,调用
mcp__linear-server__get_issue
读取其当前状态(使用
type
而非显示名称)。根据状态类型的递进顺序做出判断:
text
triage < backlog < unstarted < started < completed
canceled
(包含
Duplicate
状态)是终端状态,不在上述递进序列中。在
started
类型内,按
position
排序,因此In Progress排在In Review之前。所有转换均为幂等性:仅当当前状态在递进序列中早于解析出的目标状态时才执行转换;若issue已处于或超过目标状态,或当前类型为
completed
canceled
(终端状态不会自动推进),则静默跳过。
  • In Progress(开始处理issue时触发)——适用于
    triage
    /
    backlog
    /
    unstarted
    状态;若处于任何
    started
    completed
    canceled
    状态则跳过。
  • In Review(发布流程中PR创建/更新时触发)——适用于
    triage
    /
    backlog
    /
    unstarted
    状态,或处于
    started
    类型中position早于解析出的In Review状态的情况(例如In Progress);若处于或超过In Review状态,或
    completed
    /
    canceled
    状态则跳过。
  • Done(分支清理时触发)——适用于
    triage
    /
    backlog
    /
    unstarted
    /
    started
    状态;若处于
    completed
    /
    canceled
    状态则跳过。
使用**解析得到的状态
id
**调用
mcp__linear-server__save_issue
执行转换——该ID在重命名后仍保持明确。使用显示名称形式
state: "<target>"
仅在使用者保留Linear默认状态名称时有效。
--dry-run
模式下,跳过
save_issue
调用
。仍会调用
get_issue
读取每个issue的当前状态,并判断是否执行转换,但仅报告预期的变更(例如
A-7: Todo → In Progress (would apply)
/
A-9: In Review (would skip — already at/past target)
),而非实际执行写入操作。
Canceled
是Linear API自身使用的美式拼写——在代码或配置中引用时请保持原样。

Caller responsibilities (when / whether to fire)

调用方职责(何时/是否触发)

The skill owns the mechanics; each caller owns the policy:
  • Start of work — transition to
    In Progress
    when work begins on an issue (unless already In Progress or further along). Run automatically; no prompt.
  • Ship flow (PR open/update) — transition linked issues to
    In Review
    automatically after the PR is created or updated. No prompt.
  • Branch cleanup — transition orphaned issues to
    Done
    only after explicit confirmation, default no. Linear's GitHub integration normally handles the
    Done
    transition on PR merge, so this prompt exists only for the rare case where the integration didn't fire (e.g. the issue ID was added after merge).
本skill负责转换机制;每个调用方负责触发策略:
  • 工作启动时——开始处理issue时将其转换为
    In Progress
    状态(若已处于In Progress或更后续状态则跳过)。自动运行;无需提示。
  • 发布流程(PR创建/更新)——PR创建或更新后自动将关联的issue转换为
    In Review
    状态。无需提示。
  • 分支清理时——仅在**明确确认(默认不确认)**后,将孤立的issue转换为
    Done
    状态。Linear的GitHub集成通常会在PR合并时自动处理
    Done
    转换,因此该提示仅用于集成未触发的罕见情况(例如issue ID是在合并后添加的)。

Standalone vs inside a caller

独立运行 vs 被调用方调用

  • Standalone — resolve the target state, extract the branch's issue IDs, apply the transition, and report which issues moved and which were skipped. The default target is In Progress (the start-of-work transition that has no other home).
  • Inside a caller — the caller supplies the target (and often the
    issues
    list) and decides whether to prompt; the mechanics above are unchanged.
  • 独立运行——解析目标状态,提取分支的issue ID,执行转换,并报告哪些issue发生了变更、哪些被跳过。默认目标状态为In Progress(这是工作启动时的转换,无其他适用场景)。
  • 被调用方调用——调用方提供目标状态(通常还会提供
    issues
    列表)并决定是否提示;上述转换机制保持不变。

Implementation

实现

No supporting scripts — the skill drives the Linear MCP tools directly (
list_issue_statuses
,
get_issue
,
save_issue
). The only repo-specific inputs are the team name and the issue-ID prefixes, both read from
config.json
.
无需辅助脚本——本skill直接调用Linear MCP工具(
list_issue_statuses
get_issue
save_issue
)。唯一的仓库特定输入是团队名称和issue ID前缀,两者均从
config.json
读取。