report-issue
Draft and submit a bug report or feature request to the nexus plugin repository, using current conversation context to auto-populate details.
Files1
View on GitHubSKILL.mdskills/report-issue/SKILL.md
GitHubReport Issue
Arguments: $ARGUMENTS
Draft a GitHub issue for the nexus plugin repository based on the current conversation context. Invoke this immediately after noticing a bug or wanting to request a feature — while the context is still fresh.
Usage
/report-issue # Report a bug (default)
/report-issue --feature-request # Report a feature request
Step 1: Determine Issue Type
Parse $ARGUMENTS:
- If
--feature-requestis present →TYPE=feature-request, labelenhancement - Otherwise →
TYPE=bug, labelbug
Step 2: Early Auth Check
gh auth status 2>/dev/null
If the command fails (exit code non-zero), stop immediately:
⚠️ Not authenticated with GitHub. Run: gh auth login
Issue cannot be submitted without authentication.
Step 3: Get Plugin Version
jq -r '.plugins["nexus@claude-skills"][0].version // "unknown"' \
~/.claude/plugins/installed_plugins.json 2>/dev/null || echo "unknown"
Store as PLUGIN_VERSION.
Step 4: Build Draft from Conversation Context
Using the current conversation context, extract the following. Be concise — aim for enough detail to reproduce or understand the issue, not a full session dump.
For TYPE=bug
Extract:
- What happened — the error or unexpected behavior observed
- Error message — exact error text if visible in the conversation (quote verbatim, but redact secrets — see field instructions below)
- Context — which skill or agent was running, what the user was trying to do (general terms only, no internal hostnames or paths)
- Trigger — what action or input caused it
Per-field constraints (apply during extraction, before the Step 5 scan):
- Description: One sentence summarizing the bug. No file paths, hostnames, tokens, or PII.
- Error: Quote verbatim if available. If the error contains tokens, passwords, API keys, connection strings, email addresses, internal hostnames, or absolute file paths, replace those substrings with
[REDACTED]before including the quote. Keep the structural shape of the error (e.g.,connection refused: [REDACTED]). - Context: Describe what skill/agent was active and what the user was attempting in general terms. Do NOT include internal hostnames, service names, cluster names, or absolute file paths.
- Steps to Reproduce: Describe steps abstractly. Do NOT include credentials, absolute paths, or hostnames in step descriptions.
- Environment: Plugin version only — no OS, shell, CWD, git remote, or username.
Compose the issue body using this template:
## Description
{one-sentence summary of the bug}
## Error
{verbatim error with secrets redacted as [REDACTED]; if no error available, describe behavior}
## Context
{skill/agent that was active, what the user was attempting — general terms only}
## Steps to Reproduce
1. {step 1, abstract — no paths/hosts/credentials}
2. {step 2}
3. {add more if clear from context, otherwise omit}
## Environment
- Plugin version: {PLUGIN_VERSION}
For TYPE=feature-request
Extract:
- What capability is missing or needed — derived from what the user was trying to do
- Problem it solves — why this matters based on the conversation
Compose the issue body using this template:
## Description
{one-sentence summary of the requested feature}
## Problem
{what the user was trying to do and why the current behavior falls short}
## Proposed Behavior
{what the skill/agent/workflow should do instead}
## Context
{which skill or agent this relates to, if applicable}
## Environment
- Plugin version: {PLUGIN_VERSION}
Generate a concise issue title:
- Bug:
[bug] {skill-or-agent}: {short description}— e.g.,[bug] nexus:commit: unknown skill nexus:git-operator error - Feature:
[feature] {short description}— e.g.,[feature] report-issue: support attaching file paths
Step 5: Sensitivity Check
Before showing the draft, scan the full body and the title for sensitive content. This is a soft, LLM-driven first pass; a deterministic bash gate in Step 7 catches anything missed here.
Patterns to detect
Known prefixes (high-confidence — match by prefix anywhere in the text):
AKIA[0-9A-Z]{16},ASIA[0-9A-Z]{16}— AWS access key IDsghp_,gho_,ghu_,ghs_,ghr_followed by alphanumerics — GitHub personal access tokensxox[baprs]-followed by alphanumerics — Slack tokens-----BEGIN(any header) — private keys, certificates, PEM blocks (redact the entire block through-----END ...-----)eyJfollowed by a long base64 string with two more.-separated segments — JWTspostgres://,mysql://,mongodb://,redis://— database connection strings (especially ifuser:password@is embedded)AIza[0-9A-Za-z_-]{35}— Google API keyssk_live_…,rk_live_…,pk_live_…— Stripe live keyssk-ant-followed by alphanumerics — Anthropic API keyssk-followed by 32+ alphanumerics — OpenAI-style API keysAuthorization: Bearer …andAuthorization: Basic …— auth header values (redact the value, keep the header name)
Structural patterns (match by shape):
- Absolute paths:
/home/[name]/…,/Users/[name]/…,C:\Users\…,\\[host]\[share]\…,/mnt/c/Users/…,/private/var/folders/… .env-style assignments where the key name suggests a secret —PASSWORD=,SECRET=,TOKEN=,API_KEY=,PRIVATE_KEY=,DATABASE_URL=,DSN=,AUTH_TOKEN=— redact the value regardless of length- Email addresses matching
[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}— but keep GitHub@usernamementions (shape:@+ 1–39 alphanumerics/hyphens, no domain) - Internal hostnames or service names that weren't already part of the error/context (e.g.,
db-prod-1.corp.internal,kafka-east-cluster)
Action
For each match, replace the matched substring with [REDACTED] in both the body and the title. Preserve the surrounding text so the issue remains intelligible.
After redaction, surface a summary of what was redacted (by category, not by value):
⚠️ Sensitivity scan — redacted content:
- 1× AWS access key ID
- 2× absolute paths
- 1× email address
The draft below shows redacted content. Review before confirming.
If nothing was redacted, say so:
✓ Sensitivity scan — no patterns matched.
Step 6: Show Draft and Ask for Additions
Display the full draft:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Issue Draft
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Repo: nexus-a1/claude-skills
Type: {bug | feature-request}
Label: {bug | enhancement}
Title: {title}
{body}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Then use the AskUserQuestion tool:
header: "Add notes"
question: "Anything to add to this issue before sending?"
options: ["No, looks good", "Yes, I want to add a note"]
If the user selects "Yes", use AskUserQuestion again with an open prompt to collect their note.
If the user provides input, append it to the body under a ## Additional Notes section.
Second sensitivity scan (after notes appended)
If the user added a note, re-run the full Step 5 sensitivity check against the complete updated body (description + error + context + steps + environment + additional notes). User-supplied notes never reach gh issue create without being scanned. If new patterns are detected, redact them in place and surface a fresh summary:
⚠️ Second sensitivity scan (after Additional Notes) — redacted content:
- 1× GitHub PAT
If the user added no note, skip the second scan.
Step 7: Confirm and Submit
Render exact final payload
Show the user the exact title and body bytes that will be POSTed — not a paraphrase. This must reflect post-Step-5 (and post-Step-6 second-scan) content:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Final payload (post-redaction)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Repo: nexus-a1/claude-skills (PUBLIC — visible to anyone, indexed by search engines)
Label: {bug | enhancement}
Title: {final title}
{final body}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Confirmation
Use the AskUserQuestion tool:
header: "Confirm"
question: "Submit this issue to nexus-a1/claude-skills (public GitHub repository — will be publicly visible and indexed by search engines)?"
options: ["Yes, submit to public repo", "No, cancel"]
If No: stop and display Issue cancelled. Draft was not submitted.
Deterministic pre-submit scan gate (hard block)
If Yes: before invoking gh issue create, run a deterministic regex scan over the body and title. This is a non-LLM enforcement layer that catches secrets the Step 5 / Step 6 LLM scans may have missed. A match here hard-blocks submission.
# The draft lives in the project work area, not /tmp, and not under an mktemp
# name. Both matter, for different reasons:
# - mktemp names do not survive a Bash tool call, and neither does the
# variable holding one. The submit block below is a separate call, so it
# could never have named the file the gate scanned.
# - a PREDICTABLE name in a world-writable directory is the vector mktemp was
# avoiding: another process can pre-create or symlink /tmp/<known-name>
# between the write and the read. A private directory under .claude with
# umask 077 gives a stable name without that exposure.
umask 077
SCAN_DIR=".claude/report-issue"
mkdir -p "$SCAN_DIR"
BODY_FILE="$SCAN_DIR/body.md"
ERROR_FILE_PRE="$SCAN_DIR/error.log"
# A stable name can be pre-created as a SYMLINK by anything that can write the
# directory, and `cat >` follows it: the draft would overwrite the link target
# and then be published from it. Refuse a symlink outright, and remove any
# existing file before writing so `set -C` cannot be satisfied by a leftover.
for _f in "$BODY_FILE" "$ERROR_FILE_PRE" "$SCAN_DIR/scan-patterns.txt"; do
if [ -L "$_f" ]; then
echo "⛔ Refusing to write: $_f is a symlink. Remove it and re-run." >&2
return 1 2>/dev/null || exit 1
fi
rm -f "$_f"
done
set -C # no clobber: a file appearing between the rm and the write is a race
ERROR_FILE="$SCAN_DIR/error.log"
PATTERN_FILE="$SCAN_DIR/scan-patterns.txt"
cat > "$BODY_FILE" << 'REPORT_ISSUE_BODY_EOF'
{full issue body, post-redaction}
REPORT_ISSUE_BODY_EOF
ISSUE_TITLE='{final title, post-redaction}'
# Written to a file as well as used here, so the re-scan in the submit block
# uses THE SAME patterns rather than a second copy that can drift from this one.
SCAN_PATTERNS='AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}|ghp_[A-Za-z0-9_]+|gho_[A-Za-z0-9_]+|ghu_[A-Za-z0-9_]+|ghs_[A-Za-z0-9_]+|ghr_[A-Za-z0-9_]+|xox[baprs]-[A-Za-z0-9-]+|-----BEGIN [A-Z ]+-----|sk_live_[A-Za-z0-9]+|rk_live_[A-Za-z0-9]+|sk-ant-[A-Za-z0-9-]+|AIza[0-9A-Za-z_-]{35}'
printf '%s\n' "$SCAN_PATTERNS" > "$PATTERN_FILE"
if grep -qE "$SCAN_PATTERNS" "$BODY_FILE" || echo "$ISSUE_TITLE" | grep -qE "$SCAN_PATTERNS"; then
echo "⛔ Submission blocked — deterministic scan detected a high-confidence secret pattern in the draft." >&2
echo "Matched categories:" >&2
{ grep -oE "$SCAN_PATTERNS" "$BODY_FILE"; echo "$ISSUE_TITLE" | grep -oE "$SCAN_PATTERNS"; } \
| sed -E 's/(AKIA|ASIA)[0-9A-Z]+/AWS access key/; s/ghp_[A-Za-z0-9_]+/GitHub PAT (ghp_)/; s/gho_[A-Za-z0-9_]+/GitHub PAT (gho_)/; s/ghu_[A-Za-z0-9_]+/GitHub PAT (ghu_)/; s/ghs_[A-Za-z0-9_]+/GitHub PAT (ghs_)/; s/ghr_[A-Za-z0-9_]+/GitHub PAT (ghr_)/; s/xox[baprs]-[A-Za-z0-9-]+/Slack token/; s/-----BEGIN.*-----/PEM key block/; s/sk_live_[A-Za-z0-9]+/Stripe live key/; s/rk_live_[A-Za-z0-9]+/Stripe restricted key/; s/sk-ant-[A-Za-z0-9-]+/Anthropic API key/; s/AIza[0-9A-Za-z_-]+/Google API key/' \
| sort -u | sed 's/^/ - /' >&2
echo "" >&2
echo "The LLM redaction in Steps 5/6 missed at least one secret. Edit the conversation to remove the secret values, then re-run /report-issue." >&2
rm -f "$BODY_FILE" "$ERROR_FILE" "$PATTERN_FILE"
false
fi
If the gate hard-blocks, the skill stops here. Do not retry, do not call gh issue create. The user must remove the secret from conversation context (or paste a corrected note) and re-invoke.
Submission
If the deterministic gate passes, create the issue. The scan is re-run here,
in the same call as the publish. The gate above runs in a different Bash tool
call, and between the two the file can change — the model can edit it, a stale
file can be left at the same path by an earlier run, another process can write
to it. A scan in one call and a publish in another cannot make the claim this
gate exists to make; only a scan and a publish in the same call can, so the last
thing before gh issue create is the scan itself.
umask 077
SCAN_DIR=".claude/report-issue"
BODY_FILE="$SCAN_DIR/body.md"
ERROR_FILE="$SCAN_DIR/error.log"
PATTERN_FILE="$SCAN_DIR/scan-patterns.txt"
ISSUE_TITLE='{final title, post-redaction}'
# Same symlink refusal on the read side: publishing through a link would send
# whatever the link points at, which is not what the gate scanned.
for _f in "$BODY_FILE" "$PATTERN_FILE"; do
if [ -L "$_f" ]; then
echo "⛔ Refusing to publish: $_f is a symlink, so it is not the file that was scanned." >&2
false
fi
done
# Re-scan the exact bytes about to be published, with the patterns the gate
# wrote. Refuse if either file is missing: an unreadable scan is not a clean
# scan, and "the gate already ran" is a claim about a different call.
if [ ! -r "$BODY_FILE" ] || [ ! -r "$PATTERN_FILE" ]; then
echo "⛔ Refusing to publish: the scanned draft or its patterns are missing — re-run the gate." >&2
false
else
grep -qE -f "$PATTERN_FILE" "$BODY_FILE"
body_rc=$?
printf '%s' "$ISSUE_TITLE" | grep -qE -f "$PATTERN_FILE"
title_rc=$?
if [ "$body_rc" -eq 0 ] || [ "$title_rc" -eq 0 ]; then
echo "⛔ Refusing to publish: a secret pattern is present in the bytes about to be sent." >&2
false
elif [ "$body_rc" -ge 2 ] || [ "$title_rc" -ge 2 ]; then
echo "⛔ Refusing to publish: the pre-publish scan itself failed (grep exit $body_rc/$title_rc) — that is not a clean scan." >&2
false
else
gh issue create \
--repo "nexus-a1/claude-skills" \
--title "$ISSUE_TITLE" \
--body-file "$BODY_FILE" \
--label "{bug|enhancement}" 2>"$ERROR_FILE"
GH_RC=$?
if [ $GH_RC -ne 0 ]; then
if grep -qi "label" "$ERROR_FILE"; then
# Label doesn't exist — retry without it
gh issue create \
--repo "nexus-a1/claude-skills" \
--title "$ISSUE_TITLE" \
--body-file "$BODY_FILE"
GH_RC=$?
else
# Different error — surface it
cat "$ERROR_FILE" >&2
fi
fi
rm -f "$BODY_FILE" "$ERROR_FILE" "$PATTERN_FILE"
[ $GH_RC -eq 0 ] || false
fi
fi
Using
--body-fileavoids shell quoting issues with error messages, stack traces, and special characters in the body. The title is passed through--title "$ISSUE_TITLE"(argv, never viabash -cor string interpolation) to prevent shell-metacharacter injection. Stderr is captured to a temp file so label-not-found errors trigger a no-label fallback; cleanup runs on every path before the final exit.
Step 8: Report Result
On success:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✓ Issue Submitted
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
{issue URL}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
On failure:
⚠️ Could not create issue. gh error:
{error output}
The draft was not submitted. You can create it manually at:
https://github.com/nexus-a1/claude-skills/issues/new
Error Handling
gh not authenticated
⚠️ Not authenticated with GitHub. Run: gh auth login
No conversation context to extract
If the conversation has no discernible error or feature request to base the draft on, use the AskUserQuestion tool to ask:
header: "Describe issue"
question: "What issue would you like to report?"
Use their response as the primary input for the draft.