Notre avis
Trie les tickets d'incidents PRODINT en interrogeant les files d'attente, en analysant les descriptions et en effectuant des transitions de statut.
Points forts
- Contourne les limitations de l'API JSM en utilisant l'API REST directement
- Parse la description ADF pour extraire l'impact et les personnes affectées
- Détecte automatiquement les incidents potentiels
- Flux de triage interactif avec commentaires personnalisés
Limites
- Nécessite une CLI Jira préconfigurée et un jeton API
- Fonctionne uniquement avec le projet PRODINT
- Dépend de noms de transitions et de statuts spécifiques
Lorsque vous devez trier les tickets JSM qui vous sont assignés sans utiliser l'interface utilisateur manuelle.
Pour des projets autres que PRODINT ou lorsque les identifiants Jira ne sont pas disponibles.
Analyse de sécurité
SûrThe skill reads configuration, queries Jira via REST API, and transitions tickets using CLI commands. No destructive actions, data exfiltration, or obfuscation. All tools are used for legitimate triage tasks.
Aucun point d'attention détecté
Exemples
Check my PRODINT intake queue for new tickets.Triage ticket PRODINT-123 and move it to Accepted.Check if ticket PRODINT-456 is a potential incident based on impact and affected parties.name: product-triage description: Triage PRODINT intake tickets interactively. Query queue, assess severity, transition statuses, draft template comments, and clone accepted tickets to PYR backlog. Use when Ivy says /product-triage, "check my intake queue", "triage tickets", or "check PRODINT". allowed-tools: Bash, Read, Grep, AskUserQuestion
Payer Product Intake Triage
Triage PRODINT intake tickets assigned to you. Review queue, transition statuses, draft comments interactively, and clone accepted tickets to PYR.
Configuration
Read configuration from ~/.claude/skills/triage/defaults/config.md at the start of every invocation to get project keys, team name, field mappings, transition names, and comment templates.
Key settings:
- Intake project: PRODINT
- Delivery project: PYR
- Delivery team: Orchestration (option ID: 23029)
- Default issue type: Bug
- Default priority: Medium
Step 1: Check Prerequisites
Before querying, verify the environment:
which jira && echo "jira CLI: OK" || echo "ERROR: jira CLI not installed. Run: brew install ankitpokhrel/jira-cli/jira-cli"
[ -n "$JIRA_API_TOKEN" ] && echo "API token: OK" || echo "ERROR: JIRA_API_TOKEN not set. Add to ~/.zshrc"
If either check fails, tell the user what to fix and stop.
Step 2: Query the Intake Queue
IMPORTANT: Always use REST API v3 for PRODINT queries. The jira CLI cannot query JSM projects (permission issue). Do NOT try the CLI first.
Default query (New tickets):
JIRA_SERVER=$(grep "^server:" ~/.config/.jira/.config.yml | awk '{print $2}')
JIRA_EMAIL=$(grep "^login:" ~/.config/.jira/.config.yml | awk '{print $2}')
curl -s -H "Content-Type: application/json" \
-u "${JIRA_EMAIL}:${JIRA_API_TOKEN}" \
"${JIRA_SERVER}/rest/api/3/search/jql?jql=project%3DPRODINT%20AND%20assignee%3DcurrentUser()%20AND%20status%3D%27New%27%20ORDER%20BY%20created%20ASC&fields=summary,reporter,priority,created,status,description,attachment" | python3 -m json.tool
Other queue queries (user can request with queue review, queue needs more info, queue all):
queue review: Change status filter tostatus%3D%27In%20Review%27queue needs more info: Change status filter tostatus%3D%27Needs%20More%20Info%27queue all: Change status filter tostatus%20in%20(%27New%27%2C%27In%20Review%27%2C%27Needs%20More%20Info%27)queue accepted: Change status filter tostatus%3D%27Accepted%27
IMPORTANT: Transition names ≠ Status names in JSM.
- Use transition names with
jira issue move: "Needs Review", "Needs Info", "Accepted", "Declined" - Use status names in JQL queries: "In Review", "Needs More Info", "Accepted", "Declined"
Always include description in the fields list to parse Impact and Who Affected.
Step 2a: Parse Impact & Who Affected from ADF Description
Impact and urgency are NOT separate Jira fields. They are embedded in the description ADF from the intake form. Parse them by finding bold text labels and extracting the following text node.
Key labels to extract:
- "How is this impacting work?" → Impact value (e.g., "Blocking work (no workaround available)")
- "Who is most affected by this issue?" → Who Affected value (e.g., "Multiple teams")
- "System / Product Area:" → Product Area (e.g., "Retrieval Hub")
ADF parsing approach: Walk the ADF content tree. For each paragraph, find bold text nodes matching these labels, then capture the next non-bold text node as the value (it follows a hardBreak node).
Step 2b: Incident Flag
After parsing Impact and Who Affected, check for potential incidents:
If Impact = "Blocking work (no workaround available)" AND Who Affected = "Multiple teams", display a warning:
*** POTENTIAL INCIDENT ***
This ticket reports blocking impact across multiple teams.
Consider whether this should be handled through the formal incident process
rather than intake triage. Incidents are managed separately -- not through PRODINT.
This is a flag only — the user decides whether to escalate or continue normal triage.
Step 3: Display the Queue
Present results in this format:
PRODINT Intake Queue -- {count} ticket(s) in {status}
1. {KEY} | {Summary}
Reporter: {reporter} | Priority: {priority}
Impact: {impact} | Who Affected: {who_affected}
Attachments: {count} file(s) -- view at {jira_url} (only if attachments exist)
Created: {date} ({business_days} business day(s) ago) {aging_flag}
{incident_flag_if_applicable}
2. ...
Attachments in queue view: Include attachment in the query fields list. If a ticket has attachments, show count and a link to the ticket in Jira (e.g., "Attachments: 2 file(s) -- view at https://datavant.atlassian.net/browse/PRODINT-384"). If no attachments, omit the line entirely to keep the queue compact.
Aging Logic (Business Days Only)
Calculate business days since created date (exclude Saturdays and Sundays):
- 0 business days (created today) -> no flag
- 1 business day -> no flag
- 2+ business days -> append "-- approaching escalation window"
If the queue is empty, say:
PRODINT Intake Queue -- No tickets in {status}. You're all caught up.
Then ask: "Would you like to check tickets in other statuses? (queue review / queue needs more info / queue all)"
Step 4: Handle Triage Actions
After displaying the queue, wait for the user's instruction. They will say things like:
Queue commands:
- "queue review" or "queue needs more info" or "queue all" (query other statuses)
Single ticket actions:
- "review 42" or "review PRODINT-42"
- "review 42 -- we are looking into this" (review with acknowledgment comment)
- "need more info on 43" or "needs more info 43"
- "needs more info 43 -- need env and screenshot" (inline with context)
- "accept 42"
- "accept 42 as Story/High" (inline accept with overrides)
- "decline 44"
- "decline 44 -- not repro, tested in prod" (inline decline)
- "show 42" (view full details)
Batch ticket actions (multiple tickets at once):
- "review 385 386" or "review 385 386 -- we are investigating"
- "accept 385 386" (uses defaults for all)
- "accept 385 386 as Story/High" (same overrides applied to all)
- "decline 385 386 -- not repro, tested in prod" (same reason for all)
Always normalize ticket references: If user says just a number (e.g., "42"), prepend "PRODINT-" to make "PRODINT-42". For batch, normalize all numbers.
Action: Show (View Details)
Use REST API directly (jira CLI has inconsistent behavior with JSM):
JIRA_SERVER=$(grep "^server:" ~/.config/.jira/.config.yml | awk '{print $2}')
JIRA_EMAIL=$(grep "^login:" ~/.config/.jira/.config.yml | awk '{print $2}')
curl -s -u "${JIRA_EMAIL}:${JIRA_API_TOKEN}" \
"${JIRA_SERVER}/rest/api/3/issue/PRODINT-{number}?fields=summary,description,reporter,created,priority,status,comment,attachment" | python3 -m json.tool
Display: summary, reporter, created date, priority, Impact, Who Affected, Product Area, description details (parsed from ADF format), attachments, and any comments.
Attachments: Include attachment in the fields list. For each attachment, display:
- Filename
- Size (human-readable, e.g., "345 KB")
- Link to view:
https://{JIRA_SERVER}/browse/PRODINT-{number}(direct attachment content URLs require auth, so link to the ticket instead)
If no attachments, show "(none)".
Also check for the incident flag (Blocking + Multiple teams) and display the warning if triggered.
Action: Review (New -> Needs Review)
Supports batch and inline comments.
Basic: review 42 — no comment, just transition.
With comment: review 42 -- we are looking into this — transition + add comment.
Batch: review 385 386 — transition all tickets. Run transitions in parallel (separate Bash calls).
Batch with comment: review 385 386 -- investigating — transition + same comment on all.
For each ticket:
jira issue move PRODINT-{number} "Needs Review"
If an inline comment was provided:
jira issue comment add PRODINT-{number} "{comment}" --no-input
IMPORTANT: Never chain jira CLI commands with && or ;. The jira CLI hangs when chained. Always run comment and transition as separate Bash calls.
For batch operations, run all transitions in parallel (separate Bash tool calls), then all comments in parallel.
Confirm to user: "Moved PRODINT-{number} to Needs Review." (or summary table for batch)
Action: Need More Info (Needs Review -> Needs Info)
This is a streamlined flow. Minimize back-and-forth.
Inline mode: If the user provides context in their command (e.g., "needs more info 43 -- need env and screenshot"), use that directly. Do NOT ask again what info is needed.
Interactive mode: If the user just says "needs more info 43" with no context, ask ONE question:
Using AskUserQuestion:
- Question: "What information do you need from the reporter?"
- This is open-ended -- let the user type freely
Then, in both modes:
- Draft the comment using the template:
We need additional detail to proceed:
- {question 1}
- {question 2}
...
Once received, we will continue investigation.
Format each point as a clean bullet. Clean up grammar if needed but preserve meaning.
-
Show the drafted comment and ask: "Post this and move to Needs Info? (y/n)"
-
If confirmed, execute as separate Bash calls (never chain with &&):
# Call 1: Add comment (positional arg, NOT -m flag)
jira issue comment add PRODINT-{number} $'formatted\ncomment\nhere' --no-input
# Call 2: Transition (separate Bash call)
jira issue move PRODINT-{number} "Needs Info"
- Confirm: "Posted comment and moved PRODINT-{number} to Needs Info."
Action: Accept (Needs Review -> Accepted)
This is a single-confirmation flow. Use smart defaults, don't over-ask. Supports batch.
Defaults: Bug / Medium / Orchestration / no extra context.
Step 1: Check for inline overrides. If the user says:
- "accept 42" -> use all defaults
- "accept 42 as Story" -> override issue type only
- "accept 42 as Story/High" -> override issue type and priority
- "accept 42 -- context: affects Centene wave 4" -> add context to description
- "accept 385 386" -> batch accept with defaults (same overrides apply to all)
Step 2: Only ask if info is missing AND not using defaults. If the user said "accept 42" with no overrides, do NOT ask for issue type, priority, or context. Just use defaults.
If overrides are partially given and something is ambiguous, ask a single focused AskUserQuestion.
Batch mode: For multiple tickets (e.g., "accept 385 386"):
- Fetch all ticket details in parallel (separate Bash calls)
- Show a single confirmation listing all tickets
- Execute sequentially per ticket (create PYR → link → comment → transition) but parallelize independent steps across tickets where possible
- Show a summary table at the end
Step 3: Get PRODINT ticket details via REST API (for batch, fetch all in parallel):
JIRA_SERVER=$(grep "^server:" ~/.config/.jira/.config.yml | awk '{print $2}')
JIRA_EMAIL=$(grep "^login:" ~/.config/.jira/.config.yml | awk '{print $2}')
curl -s -u "${JIRA_EMAIL}:${JIRA_API_TOKEN}" \
"${JIRA_SERVER}/rest/api/3/issue/PRODINT-{number}?fields=summary,description" | python3 -m json.tool
Step 4: Show plan and confirm (ONE confirmation):
Accepting PRODINT-{number}:
PYR ticket: Bug | Medium | Orchestration
Summary: "{summary}"
{if additional context: "Extra context: ..."}
This will create the PYR ticket, link it, comment on PRODINT, and transition to Accepted.
Proceed? (y/n)
Step 5: If confirmed, execute in order:
- Create the PYR ticket:
~/.claude/skills/triage/scripts/create-pyr-ticket.sh \
"{summary}" \
"{description_plus_additional_context}" \
"{issue_type}" \
"{priority}"
Capture the returned PYR key from stdout.
- Link the tickets:
~/.claude/skills/triage/scripts/link-jira-tickets.sh \
"{PYR_KEY}" \
"PRODINT-{number}" \
"Cloners"
- Add the comment to PRODINT (positional arg, NOT -m flag):
jira issue comment add PRODINT-{number} "We have confirmed this issue and are moving it into the team backlog for resolution. You can track progress via the linked ticket: {PYR_KEY}." --no-input
- Transition PRODINT:
jira issue move PRODINT-{number} "Accepted"
- Verify the link is visible on the intake ticket:
JIRA_SERVER=$(grep "^server:" ~/.config/.jira/.config.yml | awk '{print $2}')
JIRA_EMAIL=$(grep "^login:" ~/.config/.jira/.config.yml | awk '{print $2}')
curl -s -u "${JIRA_EMAIL}:${JIRA_API_TOKEN}" \
"${JIRA_SERVER}/rest/api/3/issue/PRODINT-{number}?fields=issuelinks" | python3 -c "
import sys, json
data = json.loads(sys.stdin.read())
links = data.get('fields', {}).get('issuelinks', [])
found = any(
l.get('inwardIssue', {}).get('key', '').startswith('PYR') or
l.get('outwardIssue', {}).get('key', '').startswith('PYR')
for l in links
)
print('LINK_VERIFIED' if found else 'LINK_NOT_FOUND')
"
If LINK_NOT_FOUND, warn: "Link may not have been created. Please verify manually in Jira."
If any step (1-4) fails, stop and tell the user which step failed and what to do. Do NOT continue to the next step if a previous step failed. Step 5 (verification) is non-blocking — just warn if it fails.
Step 6: Confirm: "Done. Created {PYR_KEY}, linked to PRODINT-{number}, and moved to Accepted. Link verified."
Action: Decline (Needs Review -> Declined)
Minimize prompts. One question max, or zero if inline.
Inline mode: If the user provides reason in their command, use it directly. Examples:
- "decline 42 -- not reproducible, tested in prod and staging" -> draft comment immediately
- "decline 42 -- working as designed, this is expected behavior for NEM routing" -> draft immediately
Parse the inline text to determine the category (not reproducible / working as designed / already resolved) and use the rest as the explanation.
Interactive mode: If the user just says "decline 42" with no context, ask ONE question using AskUserQuestion:
- Question: "Why are you declining? Pick a reason and add a brief explanation (e.g., 'Not reproducible -- tested in prod and staging, no errors found')."
- Options: "Not reproducible", "Working as designed", "Already resolved"
- The user can type freely with a custom response that includes both reason and explanation
If the user only picks a category with no explanation, use a generic explanation (e.g., "we were unable to identify the reported issue" for not reproducible). Do NOT ask a follow-up.
Then, in both modes:
- Draft the comment using the appropriate template. Adapt opening based on decline reason:
-
Not reproducible:
We reviewed this issue and were not able to reproduce the reported behavior. Based on investigation, {explanation}. We are marking this as Declined. Please reopen if new information becomes available. -
Working as designed:
We reviewed this issue and determined it is working as designed. {explanation}. We are marking this as Declined. Please reopen if new information becomes available. -
Already resolved:
We reviewed this issue and confirmed it has been resolved. {explanation}. We are marking this as Declined. Please reopen if new information becomes available.
-
Show the drafted comment and ask: "Post this and move to Declined? (y/n)"
-
If confirmed, execute as separate Bash calls (never chain with &&):
# Call 1: Add comment (positional arg, NOT -m flag)
jira issue comment add PRODINT-{number} 'formatted comment here' --no-input
# Call 2: Transition (separate Bash call)
jira issue move PRODINT-{number} "Declined"
- Confirm: "Posted comment and moved PRODINT-{number} to Declined."
After Each Action
After each triage action, ask: "Anything else to triage, or are we done?"
If done and there are still tickets in New, remind the user: "Note: {X} ticket(s) still in New. Remember the 1-business-day SLA."
Slack Redirect
If the user mentions someone raised an issue in Slack instead of through intake, offer this response they can copy-paste:
Thanks for flagging this. Please log this via the Payer Product Support form so we can properly track and triage it. Once submitted, we'll review there.
Error Handling
- jira CLI not installed: Tell user to run
brew install ankitpokhrel/jira-cli/jira-cli - JIRA_API_TOKEN not set: Tell user to set it in ~/.zshrc
- PRODINT query fails: Use REST API v3 (never use jira CLI for PRODINT queries). If REST also fails, tell user to verify JSM Agent license and PRODINT project access.
- Transition fails: Use the exact transition names from config.md. If a name still fails, discover available transitions via REST API:
Then use the transition ID directly via REST API POST.JIRA_SERVER=$(grep "^server:" ~/.config/.jira/.config.yml | awk '{print $2}') JIRA_EMAIL=$(grep "^login:" ~/.config/.jira/.config.yml | awk '{print $2}') curl -s -u "${JIRA_EMAIL}:${JIRA_API_TOKEN}" \ "${JIRA_SERVER}/rest/api/2/issue/PRODINT-{number}/transitions" | python3 -c " import sys, json data = json.loads(sys.stdin.read()) for t in data.get('transitions', []): print(f' ID: {t[\"id\"]:5s} Name: {t[\"name\"]}')" - PYR ticket creation fails: Show the error, suggest creating manually, and provide all the details to copy.
- Link type wrong: The link script will automatically show available link types on failure. Try "Cloners" first, then "Relates".
- Comment fails: Show error, provide the comment text so user can paste it manually in Jira.
Command Reference (Quick)
| Command | What it does |
|---------|-------------|
| Queue | |
| queue review | Show tickets in In Review |
| queue needs more info | Show tickets in Needs More Info |
| queue all | Show tickets in New + In Review + Needs More Info |
| Single ticket | |
| show 42 | View full details (Impact, Who Affected, attachments) |
| review 42 | New -> Needs Review (no comment) |
| review 42 -- looking into this | New -> Needs Review + acknowledgment comment |
| needs more info 42 | In Review -> Needs More Info (asks for questions) |
| needs more info 42 -- need env and screenshot | In Review -> Needs More Info (uses inline context) |
| accept 42 | Needs Review -> Accepted (Bug/Medium defaults) |
| accept 42 as Story/High | Needs Review -> Accepted (with overrides) |
| decline 42 | Needs Review -> Declined (asks for reason) |
| decline 42 -- not repro, tested in prod | Needs Review -> Declined (uses inline reason) |
| Batch | |
| review 385 386 | Move multiple tickets to Needs Review |
| review 385 386 -- investigating | Move multiple + same comment on all |
| accept 385 386 | Accept multiple with defaults |
| accept 385 386 as Story/High | Accept multiple with same overrides |
| decline 385 386 -- not repro | Decline multiple with same reason |
Expert Next.js App Router
Developpement
Un skill qui transforme Claude en expert Next.js App Router.
Générateur de README
Developpement
Crée des README.md professionnels et complets pour vos projets.
Rédacteur de Documentation API
Developpement
Génère de la documentation API complète au format OpenAPI/Swagger.