publish
Creates a PR and updates issue tracker states (auto-detects mode from branch name)
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Creates a PR and updates issue tracker states (auto-detects mode from branch name)
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Analyzes a specific bug from the issue tracker (analysis only, no code changes)
Validate Automation Config, MCP servers, and tokens
Creates backlog epics in issue tracker from a specification document
Multi-agent discussion -- brings 2-3 agent perspectives into one conversation
Run the fix pipeline on a single ticket OR a batch -- auto-resumes from checkpoint if state exists
Implements a feature from the issue tracker -- spec, design, fix, review, test, publish
| name | publish |
| description | Creates a PR and updates issue tracker states (auto-detects mode from branch name) |
| allowed-tools | mcp__*, Bash, Read, Grep, Task |
| disable-model-invocation | true |
Publish current work: PR + (conditional) issue tracker state change. Read Automation Config from CLAUDE.md.
/publish auto-detects the publishing mode from the current branch name and the Automation Config Source Control → Branch naming template. There are three success modes (full-publish, pr-only-no-id, pr-only-404) and one failure mode (FAIL). No flags. No new config keys. The "PR-only with valid tracker reference" use case is expressed by renaming the branch to one that does not match the configured Branch naming prefix (e.g., chore/refactor-foo instead of fix/PROJ-123-foo).
Operator note (interactive-only):
/publish is interactive-only— it requires user confirmation flows in agent prose and may FAIL in environments without an MCP server configured (CI / cron). For headless / batch publishing, use/agent-flow:autopilot.
This step runs BEFORE the MCP pre-flight. It determines whether the tracker is needed at all.
0a. Resolve the current branch name.
branch_name=$(git branch --show-current)
If branch_name is empty, the working tree is in a detached HEAD state. There is no active branch to push or to use as the PR source, so /publish cannot proceed. FAIL with a single-line INFO message and EXIT non-zero:
[agent-flow][INFO] Cannot determine branch (detached HEAD). /publish requires an active branch.
This is a pre-flight environment check — NOT a tracker-down failure — so it does NOT use the [agent-flow] 🔴 Pipeline Block template. No tracker comment is posted. No webhook event is fired. (Detached HEAD is treated as FAIL — exit non-zero — NOT as pr-only-no-id, because there is no branch to push or use as PR source.)
0b. Read Source Control → Branch naming from Automation Config.
The template uses {issue-id} (and optionally {description}) as placeholders — for example fix/{issue-id}-{description} or feature/{issue-id} (per docs/reference/automation-config.md Branch naming row).
If the Branch naming key is ABSENT from Automation Config:
issue_id = null
tracker_needed = false
Emit a single logical line (single echo invocation):
[agent-flow][INFO] No Branch naming pattern configured; PR-only mode.
Jump directly to Step 3 (skip Steps 0c, 0d, 1, 2). The pipeline continues with mode = "pr-only-no-id".
0c. Identify the literal prefix preceding {issue-id}.
pre_prefix is the literal text that appears in the configured Branch naming template BEFORE the {issue-id} placeholder.
The post-{issue-id} delimiter character is intentionally NOT parsed and is NOT used as a split boundary. The "split at first delimiter" approach was abandoned in design revision 2 because the standard YouTrack/Jira/Linear ID format PROJ-123 itself contains -, which would make "split at first -" yield PROJ instead of PROJ-123. Instead, the canonical extraction regex (Step 0d) understands the structure of valid issue IDs and consumes only the issue-ID portion of the residue, ignoring any trailing description segment.
Bash idiom for prefix identification:
prefix=$(echo "$branch_naming_pattern" | sed 's/{issue-id}.*//')
Examples:
fix/{issue-id}-{description} → pre_prefix="fix/"feature/{issue-id} → pre_prefix="feature/"{issue-id} → pre_prefix=""0d. Apply the canonical issue-ID extraction regex.
If branch_name does NOT start with pre_prefix:
issue_id = nullOtherwise, strip pre_prefix from the front of branch_name to form residue, then apply the canonical extraction regex against residue:
^(#?[0-9]+|[A-Za-z][A-Za-z0-9_]*-[0-9]+)
This regex anchors at the start of residue and matches EITHER:
#?[0-9]+ — numeric, optionally #-prefixed (github / gitea / redmine shapes: 123, #42)[A-Za-z][A-Za-z0-9_]*-[0-9]+ — alphanumeric project prefix + - + digits (youtrack / jira / linear shapes: PROJ-123, ABC-456, ABC_DEF-789)Bash idiom:
if [[ "$residue" =~ ^(#?[0-9]+|[A-Za-z][A-Za-z0-9_]*-[0-9]+) ]]; then
issue_id="${BASH_REMATCH[1]}"
else
issue_id=""
fi
The first match is the issue_id. Any trailing characters in residue (e.g. -fix-crash after PROJ-123) are description text and are discarded. If the regex does not match (e.g. residue starts with non-issue-ID-shaped text), set issue_id = null.
Path-traversal defense (defensive). The canonical regex never matches a dot-only residue by construction, but as a defense-in-depth check, after extraction, if issue_id matches ^\.+$ (one-or-more dots only), set issue_id = null.
Coverage by tracker (all 6 supported types):
| Tracker | Issue ID shape | Example | Regex branch matched |
|---|---|---|---|
| youtrack | uppercase prefix + - + digits | PROJ-123 | [A-Za-z][A-Za-z0-9_]*-[0-9]+ |
| jira | uppercase prefix + - + digits | ABC-456 | [A-Za-z][A-Za-z0-9_]*-[0-9]+ |
| linear | uppercase prefix + - + digits | ENG-789 | [A-Za-z][A-Za-z0-9_]*-[0-9]+ |
| github | numeric (optionally #-prefixed) | 123 / #42 | #?[0-9]+ |
| gitea | numeric (optionally #-prefixed) | 123 / #42 | #?[0-9]+ |
| redmine | numeric (optionally #-prefixed) | 42 / #42 | #?[0-9]+ |
Worked examples (all 6 — these are the canonical reference cases):
branch="fix/PROJ-123-fix-crash", template "fix/{issue-id}-{description}"
pre_prefix="fix/"residue="PROJ-123-fix-crash"PROJ-123 → issue_id="PROJ-123"-fix-crash is description, discardedbranch="feature/PROJ-456", template "feature/{issue-id}"
pre_prefix="feature/"residue="PROJ-456"PROJ-456 → issue_id="PROJ-456"branch="chore/refactor-foo", template "fix/{issue-id}-{description}"
"fix/" → issue_id=nullbranch="fix/123-numeric-id", template "fix/{issue-id}-{description}" (github / gitea / redmine)
pre_prefix="fix/"residue="123-numeric-id"123 (numeric branch) → issue_id="123"branch="fix/#42-fix", template "fix/{issue-id}-{description}" (github hash-prefixed)
pre_prefix="fix/"residue="#42-fix"#42 → issue_id="#42"branch="feature/ABC_DEF-789", template "feature/{issue-id}" (youtrack with underscore in project key)
pre_prefix="feature/"residue="ABC_DEF-789"ABC_DEF-789 → issue_id="ABC_DEF-789"0e. Set tracker_needed and branch on the outcome.
tracker_needed = (issue_id != null)
If tracker_needed == false:
mode = "pr-only-no-id"
Emit a single logical line (single echo invocation, single \n):
[agent-flow][INFO] Branch '{branch_name}' does not match the configured Branch naming pattern. Creating PR without tracker contact.
Skip directly to Step 3 (skip Steps 1, 2). A non-matching branch is probably intentional (the user named it chore/refactor-foo); the message is INFO-level so it does not look alarming.
Otherwise (tracker_needed == true), proceed to Step 1.
tracker_needed == true)This step ONLY runs when tracker_needed == true. PR-only modes never hit it.
Type from Automation Config (Issue Tracker section).mcp__* tool matching the tracker type is accessible.error_type = "unknown" if classification cannot be made more specific) and EXIT non-zero.tracker_needed == true)This step ONLY runs when tracker_needed == true. It verifies that issue_id actually exists in the tracker and decides between full-publish, pr-only-404, and FAIL.
a. Read tracker_type from Automation Config (default: youtrack).
b. Locate the single-issue fetch tool via prefix-scan per ../../core/mcp-detection.md:28-34 and ../../core/mcp-detection.md:36 ("Scan available tools for at least one tool matching the prefix"). Do NOT hardcode tool names — pick the get_issue-shaped tool from mcp__{tracker_type}__*.
c. Call the discovered tool with issue_id.
d. Classify the outcome per the closed 5-bucket enum at ../../core/mcp-detection.md:58-87 — {tls, auth, not_found, timeout, unknown} — and branch on it:
| Outcome | mode | UX channel | Continue? |
|---|---|---|---|
| Issue returned with valid summary | "full-publish" | INFO log: [agent-flow][INFO] Issue {issue_id} found in {tracker_type}. Publishing PR + tracker update. | yes → Step 3 |
error_type == "not_found" | "pr-only-404" | 404 WARN tier (single line, NOT block channel) | yes → Step 3 |
error_type ∈ {"timeout", "auth", "tls", "unknown"} | FAIL | FAIL tier block | no — EXIT non-zero |
"Prefix has tools but no get_issue-shaped tool found" (R3 mitigation) → classify as error_type = "unknown" → FAIL.
a. Verify the current branch has commits above the base branch:
git log {base_branch}..HEAD --oneline
If zero commits → STOP with INFO: No changes to publish — branch has no commits above {base_branch}.
b. Check whether an open PR already exists for the current branch. If yes → STOP with INFO: PR already exists: {PR URL}.
Read Type from Automation Config → Issue Tracker (default: youtrack).
Before dispatch, check Agent Overrides: follow ../../core/agent-override-injector.md for publisher overrides.
You MUST invoke Task(subagent_type='agent-flow:publisher', model='haiku'). DO NOT inline-execute. The agent will commit, push, and create the PR.
Context:
Type = {Type from config}. Use MCP server for {Type}.
mode = {mode}.
issue_id = {issue_id or 'none'}.
mode == "full-publish":
Issue Tracker → State transitions → For Review).{"pr-only-no-id", "pr-only-404"}):
[agent-flow][INFO] PR-only mode ({mode}); tracker not updated.issue_id empty in PR-only modes)If Notifications → Webhook URL exists and pr-created is in On events:
curl --proto "=http,https" --max-time 5 --retry 0 -X POST -H "Content-Type: application/json" \
-d '{"event":"pr-created","issue_id":"{issue}","pr_url":"{url}","timestamp":"{ISO8601}"}' \
"{Webhook URL}"
The pr-created event fires in all non-FAIL modes. The issue_id field is the empty string when mode in {"pr-only-no-id", "pr-only-404"} — this is the forward-compatible payload contract; consumers MUST parse leniently. Failure → warning only, must not stop publish.
Tracker: row)The publisher agent emits the Publish Report. The agent's report MUST include a Tracker: row in exactly one of these three forms (defined and enforced in agents/publisher.md §82-87 — this skill prose references the contract but does not generate the report):
Tracker: Updated → For Review — emitted when mode == "full-publish"Tracker: Skipped — issue ID '{issue_id}' not found in {tracker_type} — emitted when mode == "pr-only-404"Tracker: Skipped — no issue ID in branch name — emitted when mode == "pr-only-no-id"Display the result (PR URL + issue tracker state, per the publisher agent's Publish Report).
error_type ∈ {timeout, auth, tls, unknown})This template matches the CLAUDE.md "Block Comment Template" format. The Skill: field (not Agent:) is used for skill-level blocks. The format is machine-parseable by webhook consumers.
[agent-flow] 🔴 Pipeline Block
Skill: /agent-flow:publish
Step: Tracker auto-detect (Step 2)
Reason: Cannot connect to your {tracker_type} issue tracker — cannot verify whether '{issue_id}' exists.
Detail: {error_type} error from {tracker_type} MCP: {error_message}
Recommendation:
1. Run `/agent-flow:check-setup` to diagnose tracker connectivity.
2. If you intentionally want a PR with no tracker update, rename your
branch to one that does NOT start with the configured Branch naming
prefix (e.g., from "fix/PROJ-123-foo" to "chore/PROJ-123-foo"),
then re-run /publish. Auto-detect will fall through to PR-only mode.
3. If the tracker is intentionally offline, create the PR manually:
git push -u origin {branch} && gh pr create
(or your tracker UI's equivalent)
4. Once the tracker is reachable, re-run `/agent-flow:publish`.
After emitting this block, EXIT non-zero. (/agent-flow:check-setup is the diagnostic skill; /agent-flow:setup-mcp is the configuration wizard.)
error_type == "not_found")Emit as a single echo invocation (one logical line, single trailing \n). Stdout, NOT the block channel. Pipeline continues with mode = "pr-only-404":
[agent-flow][WARN] Branch '{branch}' contains issue ID pattern '{issue_id}' but no matching ticket was found in {tracker_type}. Creating PR without tracker update.
The displayed wrapping above is a Markdown rendering artifact only — the implementation MUST emit this as ONE logical line (one echo call, no mid-line newlines).
Single logical line, single echo call. INFO level. A non-matching branch is probably intentional (user named it chore/refactor-foo); the message must not look alarming. Pipeline continues with mode = "pr-only-no-id":
[agent-flow][INFO] Branch '{branch}' does not match the configured Branch naming pattern. Creating PR without tracker contact.
Single logical line. Pipeline continues with mode = "pr-only-no-id". Skips MCP pre-flight entirely:
[agent-flow][INFO] No Branch naming pattern configured; PR-only mode.
Single logical line. Exits non-zero. NOT a Block Comment Template message — this is a pre-flight environment check, not a tracker-down failure. No tracker comment, no webhook event:
[agent-flow][INFO] Cannot determine branch (detached HEAD). /publish requires an active branch.
../../core/mcp-detection.md:28-34 — tracker prefix table (no hardcoded tool names)../../core/mcp-detection.md:36 — "Scan available tools for at least one tool matching the prefix"../../core/mcp-detection.md:58-87 — error classification (5-bucket enum + Classification Reference table)skills/fix-bugs/SKILL.md — issue-id regex ^[A-Za-z0-9#._-]+$ + dot-only rejection (the canonical extraction regex used here is a stricter superset)docs/reference/automation-config.md — Branch naming template (fix/{issue-id}-{description})agents/publisher.md §82-87 — Publish Report format (the Tracker: row contract)/publish is interactive-only. For headless / batch publishing (cron, CI, Task Scheduler), use /agent-flow:autopilot instead — it dispatches /fix-bugs or /implement-feature for each tracker-discovered issue, and those pipelines invoke publisher non-interactively from a known-tracker-backed context.