work-items-to-jira — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited work-items-to-jira (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
bash "${CLAUDE_PLUGIN_ROOT}/scripts/han-config-dir.sh" 2>/dev/null || echo "$HOME/.claude"cat .han/config.md 2>/dev/null || echo ""As your first action, use the Read tool on .han/config.md inside the personal config directory path above. A read that returns no file is no personal configuration: continue silently. When that file or the project .han/config.md probe supplies content, apply it per config-rule.md, which governs precedence between the two files, relative-path resolution, and what to do with a file that reads but cannot be used.
Take an already-broken-down work-items.md file (produced by /plan-work-items) and publish each slice as a Jira ticket in a single target project.
The breakdown work — drafting slices, assigning symbolic IDs, specifying dependencies, inventorying references — has already been done upstream. This skill's job is to validate the format, confirm the target, create one ticket per slice through the Atlassian MCP server, link the within-file dependencies, and place the tickets in the chosen column.
work-items.md that names multiple code repos still produces tickets in the single project you name; the repo prose is informational only.
Depends on line must resolve to another slice in thesame file. A Depends on that names an unknown SYM is a format error to surface for repair.
W-N, V2-N, EV-N,…); the prefix has no effect on Jira placement.
Story, no assignee, reporter taken from the Atlassian MCP identity, and the project'sinitial status (Backlog). Each is overridable per run; nothing is assigned or moved unless asked.
--parent <KEY> accepts an epic or a standard issue (astory, task, or bug). Under an epic, each item is a standard issue (default Story). Under a story (any standard issue), each item is a subtask (default the project's subtask issue type). You cannot parent under a subtask. --epic <KEY> is a deprecated alias for --parent; it resolves the same way regardless of the named issue's actual type.
references, schema docs, runbooks, ADRs, coding standards. Tickets that consume an HTTP endpoint or event payload MUST reference the contract section that defines it. Full include/exclude list in references/reference-artifact-inventory.md.
findings, team findings, facilitation summaries, gap analyses, and anything under an artifacts/ subfolder of the plan that is not a contract or design reference.
references/jira-ticket-template.md.
This skill cannot run without a configured and connected Atlassian MCP server. Confirm it is reachable by calling mcp__claude_ai_Atlassian__getAccessibleAtlassianResources to retrieve the cloud ID(s). If the tool is unavailable, the call errors, or it returns no accessible resources, stop immediately and tell the user the skill requires the Atlassian MCP server to be installed, configured, and authenticated. Do not fall back to any other publishing target.
If more than one site is accessible, note which are available; you will confirm the right one while resolving the project in Step 3.
If the path is not provided, ask for it. The input is a single work-items.md produced by /plan-work-items. Read it. Its format is described in references/work-items-file-format.md.
Read these from the arguments and conversation; do not guess defaults the user did not ask for:
--project <KEY> (e.g., ACME) or --board <name/URL>. Required. If absent, askfor it in Step 3.
--parent <KEY> (an epic like ACME-12, or a story / standard issue like ACME-34) or an issue URL.Optional. When present, every created ticket is parented to it. --epic <KEY> is accepted as a deprecated alias for the same option; if both are given and name different keys, stop and ask which one. The parent's hierarchy level decides the child issue type (resolved in Step 3): standard issues under an epic, subtasks under a story.
--type <name>. Optional; defaults to Story at the project top level or under an epic, and to theproject's subtask issue type when the parent is a story.
--assignee <accountId or email>. Optional; defaults to unassigned.--column <name>. Optional; defaults to the project's initial status (Backlog).Using the cloud ID from Step 0, resolve everything concretely now so failures surface before any ticket is created:
mcp__claude_ai_Atlassian__getVisibleJiraProjects. Ifgiven a board, resolve it to its underlying project (list projects and match; if a board maps to more than one project or is ambiguous, ask the user which project to use). If no project or board was provided, ask for one — do not proceed without a project key.
--parent, or the deprecated --epic), fetch it withmcp__claude_ai_Atlassian__getJiraIssue to confirm it exists and is in the target project, then read its issue type's hierarchy level to decide the child mode:
effective default issue type is Story.
issue type is the project's subtask type (resolved in the next bullet).
Record the parent's key and which child mode applies.
mcp__claude_ai_Atlassian__getJiraProjectIssueTypesMetadata for the project. Determine theeffective default from the parent mode above (no parent or epic parent → Story; story parent → the project's subtask issue type, the one flagged as a subtask in the metadata). Then confirm the chosen type — the --type override when given, otherwise the effective default — both exists in the project and sits at the correct hierarchy level for the parent: under a story it must be a subtask type, otherwise it must be a standard (non-subtask) type. If the chosen type is missing or at the wrong level, surface the available types at the correct level and ask the user to pick one. If a story parent is named but the project exposes no subtask type, stop and tell the user subtasks are not enabled in this project.
mcp__claude_ai_Atlassian__lookupJiraAccountId. If unset, leave tickets unassigned.
(Step 8), since transitions are per-issue.
Check the work-items file against the invariants in references/jira-ticket-template.md and references/work-items-file-format.md:
## <SYM-N> — <title> with an em-dash separator (already-publishedheadings annotated as ## <SYM-N> (<KEY>) — <title> are valid too).
**Depends on.**, trailing period, None. or comma-separated SYMs.Depends on line resolves to another slice in this file.standard, or other named artifact.
summaries, gap analyses, or anything under an artifacts/ subfolder that is not a contract or design reference.
When a check fails, attempt evidence-based repair. Pull evidence from the source work-items.md, the parent plan referenced in its intro, the feature spec in the same folder, sibling files in the plan folder, and the relevant repo's ADRs / coding standards / docs:
None. if no blockers are evident in the slice's prose. Cite the absence.None.. Cite the SYMlist this file actually defines.
External Interfaces / API Contracts section. Cite the anchor.
Visual Reference table. Cite the spec section.
decision in plain language, with a Plan decisions References bullet naming the ID plus a one-sentence description. Cite the include/exclude list.
After validation, report findings in plain language. For each finding name: (1) what is wrong — slice SYM, line reference, failing invariant; (2) the proposed fill — corrected line, new bullet, removed link; (3) the evidence — file path with line number, document section, or named source.
Then give the user three actions: Continue with fills (apply the repairs to the source work-items.md and proceed), Correct the fills (user provides the right values; apply those and proceed), or Stop (exit without creating tickets). If validation passes with no findings, proceed to Step 5.
Creating Jira tickets writes to a shared system, so confirm before doing it. Present a summary and wait for an explicit yes:
is and what each item becomes under it — a standard issue or a subtask), the issue type, the assignee (or "unassigned"), and the target column (or "Backlog (default)").
(<KEY>) annotation.| SYM | Summary (ticket title) | Depends on |
|---|---|---|
| W-1 | Backend per-list validator generalization | None |
| W-2 | … | W-1 |
State the total count and that reporter will be the authenticated Atlassian user. Do not create anything until the user confirms.
Walk the slices in file order (blocker-first, as authored). Skip any slice whose heading already carries a (<KEY>) annotation so a re-run resumes cleanly. For each remaining slice, call mcp__claude_ai_Atlassian__createJiraIssue with:
Story at the top level or under an epic; the project's subtasktype when the parent is a story),
— in the heading),Tests, Acceptance criteria). Pass it as Markdown; if the create tool requires ADF, convert it. Confirm the expected format against the tool's input schema.
story for subtask children. Use the parent field the project's create metadata exposes; for a subtask, Jira requires parent. If parent is rejected for an epic in a company-managed project, surface the legacy "Epic Link" field requirement rather than dropping the parent silently.
After each successful create, capture the returned Jira key and rewrite that slice's heading in place from ## <SYM-N> — <title> to ## <SYM-N> (<KEY>) — <title> using Edit, so dependencies resolve and re-runs skip it. Report each creation as created: <SYM-N> -> <KEY>.
Once every slice has a Jira key, build the SYM→key map from the annotated headings and walk each slice's **Depends on.** line. For each blocker (skip None.):
Depends on line in its descriptionfrom symbolic IDs to the blockers' Jira keys (linked), via mcp__claude_ai_Atlassian__editJiraIssue. This survives regardless of native-link support.
an "is blocked by" relationship from the dependent ticket to each blocker. If no issue-link tool is available, say so once in the final report rather than implying native links were made.
Report each as linked: <SYM-A>(<KEY-A>) blocked_by <SYM-B>(<KEY-B>).
By default, leave every ticket in the project's initial status (Backlog) and do nothing here.
When the user named a --column, transition each created ticket toward the matching status: call mcp__claude_ai_Atlassian__getTransitionsForJiraIssue for the ticket, find the transition whose target status matches the requested column, and apply it with mcp__claude_ai_Atlassian__transitionJiraIssue. If no transition leads to the requested column for a ticket, do not force it — report that ticket as left in Backlog and name the column it could not reach, so the user can move it by hand.
Summarize: the project and parent (if any, named as the epic or story it is), the issue type used (noting "subtask" when items were nested under a story), the assignee (or unassigned), and the column. List every created ticket as <SYM-N> — <KEY> with its URL, the count of dependency links made (and whether they are native Jira links or description references), and any slices skipped because they already carried a key. If any step failed, report the error and confirm the source work-items.md annotations reflect exactly which tickets were created.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.