feat(triage): dual MCP/REST mode with shared netrc credentials
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: triage
|
name: triage
|
||||||
description: Use when converting a bug report or feature request — a screenshot and/or a short message, typically forwarded from the company Telegram chat — into a Jira issue routed to the right developer. Triggers on /gw-triage:triage, "crea una issue da questo", "segnala questo bug", or a pasted/dropped Telegram report with intent to track it.
|
description: Use when converting a bug report or feature request — a screenshot and/or a short message, typically forwarded from the company Telegram chat — into a Jira issue routed to the right developer. Triggers on /gw-triage:triage, $triage, "crea una issue da questo", "segnala questo bug", or a pasted/dropped Telegram report with intent to track it.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Triage — da segnalazione a issue Jira
|
# Triage — da segnalazione a issue Jira
|
||||||
@@ -11,26 +11,63 @@ well-formed Jira issue assigned to the right developer.
|
|||||||
**All interaction with the user happens in Italian.** This file is
|
**All interaction with the user happens in Italian.** This file is
|
||||||
English for precision; never quote it to the user.
|
English for precision; never quote it to the user.
|
||||||
|
|
||||||
The Atlassian MCP tools referenced below live under the
|
## Environment
|
||||||
`mcp__plugin_atlassian_atlassian__` prefix. If they are deferred, load
|
|
||||||
them in ONE ToolSearch call before Step 0:
|
- **Root**: `${CLAUDE_PLUGIN_ROOT}` if that variable is set; otherwise
|
||||||
`select:mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources,mcp__plugin_atlassian_atlassian__searchJiraIssuesUsingJql,mcp__plugin_atlassian_atlassian__createJiraIssue,mcp__plugin_atlassian_atlassian__addCommentToJiraIssue`
|
the real path of this skill's own directory (resolve symlinks, e.g.
|
||||||
|
with `realpath`), two levels up. `config/` and `knowledge/` below
|
||||||
|
are relative to this root.
|
||||||
|
- **Netrc**: `~/.config/gw-triage/netrc` — Jira credentials, written
|
||||||
|
by the `setup` skill.
|
||||||
|
- **Mode** — how Jira is reached:
|
||||||
|
- **MCP mode** (Claude Code): the Atlassian MCP tools (prefix
|
||||||
|
`mcp__plugin_atlassian_atlassian__`) are available. If they are
|
||||||
|
deferred, load them in ONE ToolSearch call before Step 0:
|
||||||
|
`select:mcp__plugin_atlassian_atlassian__getAccessibleAtlassianResources,mcp__plugin_atlassian_atlassian__searchJiraIssuesUsingJql,mcp__plugin_atlassian_atlassian__createJiraIssue,mcp__plugin_atlassian_atlassian__addCommentToJiraIssue`
|
||||||
|
- **REST mode** (Codex, or any platform without those tools): curl
|
||||||
|
against the Jira Cloud REST API v2, authenticated with
|
||||||
|
`--netrc-file ~/.config/gw-triage/netrc`. `<site>` in the URLs
|
||||||
|
below is the `site` value from routing.json; `cloudId` is not
|
||||||
|
used in REST mode.
|
||||||
|
|
||||||
|
Exception: on Claude Code (recognizable by `CLAUDE_PLUGIN_ROOT`
|
||||||
|
being set), MCP is the intended path — if the tools are missing, do
|
||||||
|
NOT fall back to REST; stop with the install guide in Step 0.
|
||||||
|
|
||||||
|
For REST calls that send a JSON body, write the body to a temp file
|
||||||
|
first (`mktemp`) and pass it with `-d @<file>` — never inline JSON
|
||||||
|
with user text on the command line.
|
||||||
|
|
||||||
## Step 0 — Preflight
|
## Step 0 — Preflight
|
||||||
|
|
||||||
|
**MCP mode**
|
||||||
|
|
||||||
1. Call `getAccessibleAtlassianResources`. If the tool is missing or
|
1. Call `getAccessibleAtlassianResources`. If the tool is missing or
|
||||||
returns an authentication error, STOP and print this guide in
|
returns an authentication error, STOP and print this guide in
|
||||||
Italian, then end the turn:
|
Italian, then end the turn:
|
||||||
- installa il plugin Atlassian: `/plugin install atlassian@claude-plugins-official`
|
- installa il plugin Atlassian: `/plugin install atlassian@claude-plugins-official`
|
||||||
- esegui `/mcp`, seleziona `atlassian` e completa il login nel browser con l'account aziendale
|
- esegui `/mcp`, seleziona `atlassian` e completa il login nel browser con l'account aziendale
|
||||||
- poi rilancia `/gw-triage:triage`
|
- poi rilancia `/gw-triage:triage`
|
||||||
2. Run `test -n "$JIRA_EMAIL" && test -n "$JIRA_API_TOKEN" && echo ok || echo missing`.
|
2. Attachment credentials:
|
||||||
If `missing`: check whether `~/.claude/settings.json` has
|
`test -r ~/.config/gw-triage/netrc && echo netrc || (test -n "$JIRA_EMAIL" && test -n "$JIRA_API_TOKEN" && echo env || echo missing)`.
|
||||||
`env.JIRA_EMAIL`/`env.JIRA_API_TOKEN`. If yes, the session predates
|
Remember the answer for Step 7. If `missing`, tell the user
|
||||||
the config: tell the user (Italian) to restart Claude Code to enable
|
(Italian) the screenshot cannot be attached automatically and that
|
||||||
automatic attachments. If no, tell them the screenshot cannot be
|
`/gw-triage:setup` configures it in a couple of minutes. Say it
|
||||||
attached automatically and that `/gw-triage:setup` configures it in
|
once and proceed.
|
||||||
a couple of minutes. Either way, say it once and proceed.
|
|
||||||
|
**REST mode** — the netrc is required; it is the only transport.
|
||||||
|
|
||||||
|
1. `test -r ~/.config/gw-triage/netrc && echo ok || echo missing` —
|
||||||
|
if `missing`, STOP and print in Italian: serve il token API Jira;
|
||||||
|
esegui `$setup` e poi rilancia `$triage`.
|
||||||
|
2. Verify:
|
||||||
|
|
||||||
|
curl -s -o /dev/null -w "%{http_code}" \
|
||||||
|
--netrc-file ~/.config/gw-triage/netrc \
|
||||||
|
<site>/rest/api/2/myself
|
||||||
|
|
||||||
|
Anything but `200` → STOP (Italian): token scaduto o revocato,
|
||||||
|
rilancia `$setup`.
|
||||||
|
|
||||||
## Step 1 — Input
|
## Step 1 — Input
|
||||||
|
|
||||||
@@ -44,8 +81,8 @@ Expect a short message (usually Italian) and optionally a screenshot.
|
|||||||
|
|
||||||
## Step 2 — Load routing and knowledge
|
## Step 2 — Load routing and knowledge
|
||||||
|
|
||||||
Read `${CLAUDE_PLUGIN_ROOT}/config/routing.json` and every `.md` file
|
Read `<root>/config/routing.json` and every `.md` file in
|
||||||
in `${CLAUDE_PLUGIN_ROOT}/knowledge/` except `README.md`.
|
`<root>/knowledge/` except `README.md`.
|
||||||
|
|
||||||
## Step 3 — Classify
|
## Step 3 — Classify
|
||||||
|
|
||||||
@@ -64,20 +101,46 @@ question in Italian (e.g. "Succede solo su iOS o anche su Android?").
|
|||||||
Never more than one question; otherwise decide and state the assumption
|
Never more than one question; otherwise decide and state the assumption
|
||||||
in the draft.
|
in the draft.
|
||||||
|
|
||||||
If no knowledge profile covers the symptoms, classify using the routing.json area list alone and state the lower confidence explicitly in the draft.
|
If no knowledge profile covers the symptoms, classify using the
|
||||||
|
routing.json area list alone and state the lower confidence explicitly
|
||||||
|
in the draft.
|
||||||
|
|
||||||
## Step 4 — Duplicate check
|
## Step 4 — Duplicate check
|
||||||
|
|
||||||
Extract 2–4 distinctive keywords (include Italian and English variants
|
Extract 2–4 distinctive keywords (include Italian and English variants
|
||||||
of domain terms). Call `searchJiraIssuesUsingJql` with:
|
of domain terms). Search with JQL
|
||||||
`project = <projectKey> AND text ~ "<keywords>" AND statusCategory != Done ORDER BY created DESC`
|
`project = <projectKey> AND text ~ "<keywords>" AND statusCategory != Done ORDER BY created DESC`
|
||||||
(maxResults 10). Judge similarity by symptom, not wording.
|
(maxResults 10):
|
||||||
|
|
||||||
|
- MCP mode: `searchJiraIssuesUsingJql`.
|
||||||
|
- REST mode (note: search is the one call NOT on v2 — Atlassian
|
||||||
|
removed `/rest/api/2/search` in 2025; `/rest/api/3/search/jql` is
|
||||||
|
its replacement):
|
||||||
|
|
||||||
|
curl -s --netrc-file ~/.config/gw-triage/netrc -G \
|
||||||
|
--data-urlencode 'jql=<the JQL above>' \
|
||||||
|
--data-urlencode 'maxResults=10' \
|
||||||
|
--data-urlencode 'fields=summary,status,issuetype' \
|
||||||
|
"<site>/rest/api/3/search/jql"
|
||||||
|
|
||||||
|
Judge similarity by symptom, not wording.
|
||||||
|
|
||||||
If a likely duplicate exists: show it (key, title, status) and ask
|
If a likely duplicate exists: show it (key, title, status) and ask
|
||||||
(Italian) whether to comment on it instead of creating a new issue.
|
(Italian) whether to comment on it instead of creating a new issue.
|
||||||
If yes → `addCommentToJiraIssue` with the new report's details (and
|
If yes, add a comment with the new report's details (and note the
|
||||||
note the screenshot can't be attached to a comment automatically),
|
screenshot can't be attached to a comment automatically), report the
|
||||||
report the link, stop.
|
link, stop:
|
||||||
|
|
||||||
|
- MCP mode: `addCommentToJiraIssue`.
|
||||||
|
- REST mode — write `{"body": "<comment text>"}` to a temp file, then:
|
||||||
|
|
||||||
|
curl -s -o /tmp/gw-triage-comment.json -w "%{http_code}" \
|
||||||
|
--netrc-file ~/.config/gw-triage/netrc \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-X POST -d @<temp-file> \
|
||||||
|
"<site>/rest/api/2/issue/<KEY>/comment"
|
||||||
|
|
||||||
|
Success = HTTP 201.
|
||||||
|
|
||||||
## Step 5 — Draft (Italian)
|
## Step 5 — Draft (Italian)
|
||||||
|
|
||||||
@@ -100,9 +163,33 @@ epic and assignee).
|
|||||||
|
|
||||||
## Step 6 — Create
|
## Step 6 — Create
|
||||||
|
|
||||||
Call `createJiraIssue` with: `cloudId`, `projectKey`, `issueTypeName`
|
- MCP mode: call `createJiraIssue` with `cloudId`, `projectKey`,
|
||||||
(`Bug` or `Task`), `summary`, `description`, the area's
|
`issueTypeName` (`Bug` or `Task`), `summary`, `description`, the
|
||||||
`assignee.accountId`, and `additional_fields: {"parent": {"key": "<area epic>"}}`.
|
area's `assignee.accountId`, and
|
||||||
|
`additional_fields: {"parent": {"key": "<area epic>"}}`.
|
||||||
|
- REST mode — write this JSON to a temp file (description as plain
|
||||||
|
text; Jira wiki markup like `h3.` headings and `*bold*` is allowed):
|
||||||
|
|
||||||
|
{
|
||||||
|
"fields": {
|
||||||
|
"project": {"key": "<projectKey>"},
|
||||||
|
"issuetype": {"name": "<Bug|Task>"},
|
||||||
|
"summary": "<titolo>",
|
||||||
|
"description": "<descrizione>",
|
||||||
|
"assignee": {"accountId": "<area accountId>"},
|
||||||
|
"parent": {"key": "<area epic>"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Then:
|
||||||
|
|
||||||
|
curl -s -o /tmp/gw-triage-create.json -w "%{http_code}" \
|
||||||
|
--netrc-file ~/.config/gw-triage/netrc \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-X POST -d @<temp-file> \
|
||||||
|
"<site>/rest/api/2/issue"
|
||||||
|
|
||||||
|
Success = HTTP 201; the issue key is in the response file.
|
||||||
|
|
||||||
If the call fails, report the raw error with a one-line Italian
|
If the call fails, report the raw error with a one-line Italian
|
||||||
summary and stop — nothing partial to clean up (the attachment step
|
summary and stop — nothing partial to clean up (the attachment step
|
||||||
@@ -110,17 +197,22 @@ only runs after successful creation).
|
|||||||
|
|
||||||
## Step 7 — Attach screenshot
|
## Step 7 — Attach screenshot
|
||||||
|
|
||||||
Only if Step 0 found the env vars AND the screenshot is a file path:
|
Only if Step 0 found credentials (netrc or legacy env) AND the
|
||||||
|
screenshot is a file path:
|
||||||
|
|
||||||
curl -s -o /tmp/gw-triage-attach.json -w "%{http_code}" -X POST \
|
curl -s -o /tmp/gw-triage-attach.json -w "%{http_code}" -X POST \
|
||||||
-H "X-Atlassian-Token: no-check" \
|
-H "X-Atlassian-Token: no-check" \
|
||||||
-u "$JIRA_EMAIL:$JIRA_API_TOKEN" \
|
--netrc-file ~/.config/gw-triage/netrc \
|
||||||
-F "file=@<screenshot-path>" \
|
-F "file=@<screenshot-path>" \
|
||||||
"<site>/rest/api/3/issue/<ISSUE-KEY>/attachments"
|
"<site>/rest/api/3/issue/<ISSUE-KEY>/attachments"
|
||||||
|
|
||||||
`<site>` comes from routing.json. Success = HTTP 200. Any failure is
|
If Step 0 answered `env` (legacy install, no netrc), replace the
|
||||||
non-fatal: tell the user (Italian) to drag the image into the Jira
|
`--netrc-file` line with `-u "$JIRA_EMAIL:$JIRA_API_TOKEN"`, leaving
|
||||||
issue manually.
|
the `$` references unexpanded for the shell — never substitute the
|
||||||
|
values yourself.
|
||||||
|
|
||||||
|
Success = HTTP 200. Any failure is non-fatal: tell the user (Italian)
|
||||||
|
to drag the image into the Jira issue manually.
|
||||||
|
|
||||||
## Step 8 — Report
|
## Step 8 — Report
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user