| name | create-issue |
| description | Create a GitHub issue in camunda/camunda with the correct template, component label, and parent link. Use when asked to create, file, or open an issue — for bugs, features, tasks, tech debt, or CVEs. |
Create Issue
Creates a well-formed GitHub issue in camunda/camunda following the official templates. Prevents
missing parent links, wrong component labels, and skipped required fields.
Prerequisites
gh auth status
Procedure
Step 1 — Read available templates
At invocation time, list the templates from the filesystem:
ls .github/ISSUE_TEMPLATE/*.yml
The ls output from above is the authoritative list of available templates. The table below is a
default mapping for the common cases — if a template appears in the filesystem but not here, read
its labels: field to infer the kind/ label.
| User intent | Template file | kind/ label |
|---|
| Bug, defect, crash, regression | 1. bug_report.yml | kind/bug |
| Feature, improvement | 2. feature_request.yml | kind/feature-request |
| Task, activity, chore | 3. task.yml | kind/task |
| Epic breakdown | 4. epic breakdown.yml | kind/epic |
| CVE, security vulnerability | 5. CVE.yml | kind/cve |
| Tech debt, cleanup | 6. tech debt.yml | kind/tech-debt |
If the issue type is ambiguous, ask the user to pick one before proceeding.
Step 2 — Read the template and collect field values
Read the chosen template:
cat ".github/ISSUE_TEMPLATE/<chosen>.yml"
For bug reports (1. bug_report.yml), invoke the grill-me skill before inferring fields — it
builds shared understanding of impact, root cause, and reproduction path, making field inference
much more accurate.
Ask the user for a free-form description of the issue. Use that description to infer values for
every field in the template's body: list. For each item:
- Skip
type: markdown items (they are display-only).
- Infer a value from the description where possible — including title, dropdown selections, and
prose sections.
- Only ask the user a follow-up question when the description does not provide enough signal to
fill a field confidently. Batch all uncertain fields into a single follow-up rather than asking
one at a time.
- If
validations.required: true and a value cannot be inferred, the user must provide one before
proceeding.
- If
validations.required: false or absent and nothing can be inferred, leave the field empty.
Step 3 — Infer the component label
Detect which module the issue relates to from the current working directory and modified files:
git diff --name-only HEAD
Map path prefixes to component labels:
| Path prefix | Component label |
|---|
zeebe/ — see below | component/zeebe-engine or component/zeebe-platform (fallback: component/zeebe) |
operate/ | component/operate |
tasklist/ | component/tasklist |
identity/ | component/identity |
optimize/ | component/optimize |
clients/ | component/clients |
webapp/ | component/c8-api |
gateways/gateway-mcp/ | component/mcp |
gateways/ | component/gateway |
db/ or search/ | component/data-layer |
document/ | component/document-handling |
testing/ | component/camunda-process-test |
qa/ | component/qa |
.github/ or .claude/ | component/build-pipeline |
Zeebe label split — use the conceptual layer, not the path:
component/zeebe-engine — application/engine layer: BPMN/DMN execution, process state, variable
handling, FEEL evaluation, gRPC/REST API surface, zeebe/gateway, zeebe/engine
component/zeebe-platform — platform/infrastructure layer: broker internals, clustering, Raft,
replication, storage (RocksDB), job streaming, zeebe/broker (infra aspects)
- Grey areas (e.g.
zeebe/broker engine logic, job streaming in zeebe/gateway): prefer the
layer that is more affected; use component/zeebe as fallback when genuinely unclear
When multiple modules are touched, pick the label for the most-affected one. If uncertain, ask the
user. If no files are modified, ask the user which component applies.
Step 4 — Ask for the parent issue
Ask the user:
"Does this issue have a parent issue or epic? If so, please provide the issue number or URL."
If they provide one, verify it exists and fetch its title for display in the summary:
gh issue view <number> --repo camunda/camunda --json number,title,url
If the user says there is no parent, omit the parent link from the body.
Step 5 — Compose the issue body
Build the Markdown body directly from the template read in Step 2, mirroring exactly what the
GitHub form UI produces. For each item in the template's body: list:
- Skip
type: markdown items (display-only).
- For
type: textarea items: render attributes.label as a ### Heading and the user's value as
the content.
- For
type: dropdown items: render attributes.label as a ### Heading and the selected
option's text verbatim as the content (e.g. <!-- Zeebe- -->). These HTML comment markers are
what .github/opened_issue_labeler.yml scans on issue open to auto-apply labels.
Append a Links section at the end with the parent issue URL if one was provided.
When the body points at specific lines of repository code, use a stable GitHub permalink rather
than a bare path:line (see "Referencing code in issues, PRs, and comments" in AGENTS.md).
Do not invent sections or use any structure other than what the template defines.
Step 6 — Propose summary and ask for approval
Before printing the summary, scan the body for sensitive data and redact it: replace UUIDs,
customer/user IDs, internal hostnames, and email addresses found in any copied log output with
<redacted>.
Print a clear summary and wait for explicit confirmation. Do NOT call gh issue create yet.
📋 Issue summary
────────────────────────────────────────────
Title: <proposed title>
Type: <bug | feature | task | tech-debt | CVE>
Labels: kind/<type> (component/*, severity/*, likelihood/* applied automatically via labeler)
Parent: https://github.com/camunda/camunda/issues/<N> (or "none")
Body:
<full proposed body>
────────────────────────────────────────────
Create this issue? (y/N)
If the user requests changes, revise the summary and ask again before proceeding.
Step 7 — Create the issue and register the parent relationship
After the user confirms, create the issue. Pass only the kind/<type> label explicitly — all
other labels (component/*, severity/*, likelihood/*, affects/*) are applied automatically
by .github/opened_issue_labeler.yml, which scans the body for the HTML comment markers rendered
in Step 5.
body_file=$(mktemp)
printf '%s' "<body>" > "$body_file"
gh issue create \
--repo camunda/camunda \
--title "<title>" \
--body-file "$body_file" \
--label "kind/<type>"
rm -f "$body_file"
Capture the new issue number from the returned URL. Then fetch its database ID and node ID (both
needed below — the database ID for the sub-issues API, the node ID for setting the Issue Type):
gh api repos/camunda/camunda/issues/<new-issue-number> --jq '{id: .id, node_id: .node_id}'
Set the native GitHub Issue Type (bypassed by gh issue create) by resolving the type name from
the template's type: field against the repo's configured issue types:
type_id=$(gh api graphql -f query='{
repository(owner: "camunda", name: "camunda") {
issueTypes(first: 20) { nodes { id name } }
}
}' --jq --arg t "<type from template>" \
'.data.repository.issueTypes.nodes[]
| select(.name | ascii_downcase == ($t | ascii_downcase)) | .id')
gh api graphql -f query="mutation {
updateIssue(input: {id: \"<node_id>\", issueTypeId: \"$type_id\"}) {
issue { id }
}
}"
If a parent was provided, register the native GitHub sub-issue relationship so the new issue
appears as a child of the parent in the GitHub UI:
gh api \
--method POST \
repos/camunda/camunda/issues/<parent-number>/sub_issues \
--input - <<< "{\"sub_issue_id\": <new-issue-database-id>}"
Print the URL of the created issue.