Aiwg issue-update

Update existing ticket/issue with status changes, comments, or field updates

install
source · Clone the upstream repo
git clone https://github.com/jmagly/aiwg
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/jmagly/aiwg "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agentic/code/frameworks/sdlc-complete/skills/issue-update" ~/.claude/skills/jmagly-aiwg-issue-update-0bbf6b && rm -rf "$T"
manifest: agentic/code/frameworks/sdlc-complete/skills/issue-update/SKILL.md
source content

Issue Update

Purpose

Update an existing ticket/issue with status transitions, progress comments, assignee changes, or metadata updates. Automatically uses the configured ticketing provider (Gitea, GitHub, Jira, Linear) or local file-based tracking.

Task

Given a ticket ID and update parameters:

  1. Load configuration from
    .aiwg/config.yaml
    or project
    CLAUDE.md
  2. Validate ticket exists on provider or in local files
  3. Apply updates (status, comment, assignee, labels, priority)
  4. Return confirmation with updated ticket details

Parameters

  • <ticket-id>
    (required): Issue identifier (e.g.,
    ISSUE-001
    ,
    #42
    ,
    PROJECT-123
    ,
    ENG-456
    )
  • --status STATE
    (optional): Update ticket status
    • Valid states:
      open
      ,
      in_progress
      ,
      closed
      ,
      blocked
      ,
      review
    • Provider-specific mappings applied automatically
  • --comment "text"
    (optional): Add progress comment or note
  • --assignee USER
    (optional): Assign to user (or
    unassigned
    to clear)
  • --labels "label1,label2"
    (optional): Replace labels (comma-separated)
  • --add-labels "label1,label2"
    (optional): Add labels without replacing existing
  • --remove-labels "label1,label2"
    (optional): Remove specific labels
  • --priority LEVEL
    (optional): Update priority (low|medium|high|critical)
  • --milestone NAME
    (optional): Associate with milestone (provider-dependent)
  • --provider NAME
    (optional): Override configured provider

Inputs

Configuration sources (same as

/issue-create
):

  1. .aiwg/config.yaml
    - Project-level configuration
  2. CLAUDE.md
    - User-level configuration
  3. Default:
    local
    provider

Outputs

All Providers:

  • Confirmation of update
  • Updated ticket details
  • URL or file path to view changes

Workflow

Step 1: Parse Parameters

Extract from command invocation:

# Update status
/issue-update ISSUE-001 --status in_progress

# Add comment
/issue-update ISSUE-001 --comment "Completed authentication module, working on authorization next"

# Update status with comment
/issue-update ISSUE-001 --status closed --comment "Fixed in commit abc123"

# Assign ticket
/issue-update ISSUE-001 --assignee johndoe

# Update multiple fields
/issue-update ISSUE-001 --status in_progress --assignee johndoe --priority high --comment "Started implementation"

# Add labels without replacing
/issue-update ISSUE-001 --add-labels "urgent,needs-review"

# Remove labels
/issue-update ISSUE-001 --remove-labels "wip,blocked"

# Replace all labels
/issue-update ISSUE-001 --labels "completed,tested"

Parameter extraction:

  • Issue ID: First argument (required)
  • Flags: Parse
    --flag value
    pairs
  • At least one update flag required (status, comment, assignee, labels, priority)

Step 2: Load Configuration

Same as

/issue-create
command:

  1. Check
    .aiwg/config.yaml
  2. Fallback to
    CLAUDE.md
  3. Default to
    local
    provider

Override with

--provider
if specified.

Step 3: Validate Issue Exists

Gitea:

# Fetch issue to verify existence
curl -s -H "Authorization: token $(cat ~/.config/gitea/token)" \
  "${URL}/api/v1/repos/${OWNER}/${REPO}/issues/${TICKET_NUM}"

# Check HTTP status: 200 = exists, 404 = not found

GitHub:

# Fetch issue via gh CLI
gh issue view "${TICKET_NUM}" --repo "${OWNER}/${REPO}"

# Exit code 0 = exists, non-zero = not found

Jira:

# Fetch issue via REST API
curl -s -u "${JIRA_EMAIL}:${JIRA_API_TOKEN}" \
  "${JIRA_URL}/rest/api/3/issue/${ISSUE_KEY}"

# Check HTTP status: 200 = exists, 404 = not found

Linear:

# Fetch issue via GraphQL
curl -s -X POST https://api.linear.app/graphql \
  -H "Authorization: ${LINEAR_API_TOKEN}" \
  -d '{"query": "query { issue(id: \"'${ISSUE_ID}'\") { id identifier title } }"}'

# Check if issue returned in response

Local:

# Check if file exists
if [ -f ".aiwg/issues/${TICKET_ID}.md" ]; then
  echo "Issue exists"
else
  echo "Issue not found"
fi

Error if not found:

❌ Issue not found: ISSUE-001

Available tickets:
- ISSUE-002: Add dark mode
- ISSUE-003: Fix navigation bug

List all tickets: /issue-list

Step 4: Map Status to Provider-Specific Values

Status mapping table:

GenericGiteaGitHubJiraLinearLocal
open
openopenTo DoBacklogopen
in_progress
openopenIn ProgressIn Progressin_progress
closed
closedclosedDoneDoneclosed
blocked
openopenBlockedBlockedblocked
review
openopenIn ReviewIn Reviewreview

Implementation:

case "${PROVIDER}" in
  gitea|github)
    case "${STATUS}" in
      closed) PROVIDER_STATUS="closed" ;;
      *) PROVIDER_STATUS="open" ;;
    esac
    ;;
  jira)
    case "${STATUS}" in
      open) PROVIDER_STATUS="To Do" ;;
      in_progress) PROVIDER_STATUS="In Progress" ;;
      closed) PROVIDER_STATUS="Done" ;;
      blocked) PROVIDER_STATUS="Blocked" ;;
      review) PROVIDER_STATUS="In Review" ;;
    esac
    ;;
  linear)
    case "${STATUS}" in
      open) PROVIDER_STATUS="Backlog" ;;
      in_progress) PROVIDER_STATUS="In Progress" ;;
      closed) PROVIDER_STATUS="Done" ;;
      blocked) PROVIDER_STATUS="Blocked" ;;
      review) PROVIDER_STATUS="In Review" ;;
    esac
    ;;
  local)
    PROVIDER_STATUS="${STATUS}"
    ;;
esac

Note: Gitea and GitHub don't have separate "in_progress" state - use labels instead (e.g., add "in-progress" label when status changes to

in_progress
)

Step 5: Update Issue (Provider-Specific)

Gitea

Use MCP tools

mcp__gitea__edit_issue
and
mcp__gitea__create_issue_comment
:

Edit issue metadata:

# Update status (state)
# Use mcp__gitea__edit_issue
# Parameters:
#   owner: ${OWNER}
#   repo: ${REPO}
#   issue_number: ${TICKET_NUM}
#   state: "open" | "closed"
#   assignee: ${ASSIGNEE} (optional)
#   labels: [${LABELS}] (optional)

Add comment:

# Use mcp__gitea__create_issue_comment
# Parameters:
#   owner: ${OWNER}
#   repo: ${REPO}
#   issue_number: ${TICKET_NUM}
#   body: ${COMMENT}

Combined workflow:

  1. If status/assignee/labels specified → use
    mcp__gitea__edit_issue
  2. If comment specified → use
    mcp__gitea__create_issue_comment
  3. Both can be called in sequence if needed

GitHub

Use

gh
CLI:

Update status:

# Open issue
gh issue reopen "${TICKET_NUM}" --repo "${OWNER}/${REPO}"

# Close issue
gh issue close "${TICKET_NUM}" --repo "${OWNER}/${REPO}"

Add comment:

gh issue comment "${TICKET_NUM}" \
  --repo "${OWNER}/${REPO}" \
  --body "${COMMENT}"

Update assignee:

# Assign
gh issue edit "${TICKET_NUM}" \
  --repo "${OWNER}/${REPO}" \
  --add-assignee "${ASSIGNEE}"

# Unassign
gh issue edit "${TICKET_NUM}" \
  --repo "${OWNER}/${REPO}" \
  --remove-assignee "${ASSIGNEE}"

Update labels:

# Add labels
gh issue edit "${TICKET_NUM}" \
  --repo "${OWNER}/${REPO}" \
  --add-label "label1,label2"

# Remove labels
gh issue edit "${TICKET_NUM}" \
  --repo "${OWNER}/${REPO}" \
  --remove-label "label1,label2"

Jira

Use Jira REST API v3:

Update issue fields:

cat > /tmp/jira-update.json <<EOF
{
  "fields": {
    "status": {
      "name": "${PROVIDER_STATUS}"
    },
    "assignee": {
      "name": "${ASSIGNEE}"
    },
    "priority": {
      "name": "${PRIORITY}"
    },
    "labels": [${LABELS_JSON}]
  }
}
EOF

curl -X PUT "${JIRA_URL}/rest/api/3/issue/${ISSUE_KEY}" \
  -u "${JIRA_EMAIL}:${JIRA_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d @/tmp/jira-update.json

Add comment:

cat > /tmp/jira-comment.json <<EOF
{
  "body": {
    "type": "doc",
    "version": 1,
    "content": [
      {
        "type": "paragraph",
        "content": [
          {
            "type": "text",
            "text": "${COMMENT}"
          }
        ]
      }
    ]
  }
}
EOF

curl -X POST "${JIRA_URL}/rest/api/3/issue/${ISSUE_KEY}/comment" \
  -u "${JIRA_EMAIL}:${JIRA_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d @/tmp/jira-comment.json

Linear

Use Linear GraphQL API:

Update issue:

cat > /tmp/linear-update.json <<EOF
{
  "query": "mutation IssueUpdate(\$issueId: String!, \$stateId: String, \$assigneeId: String, \$priority: Int) { issueUpdate(id: \$issueId, input: { stateId: \$stateId, assigneeId: \$assigneeId, priority: \$priority }) { success issue { id identifier url state { name } } } }",
  "variables": {
    "issueId": "${ISSUE_ID}",
    "stateId": "${STATE_ID}",
    "assigneeId": "${ASSIGNEE_ID}",
    "priority": ${PRIORITY_NUM}
  }
}
EOF

curl -X POST https://api.linear.app/graphql \
  -H "Authorization: ${LINEAR_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d @/tmp/linear-update.json

Add comment:

cat > /tmp/linear-comment.json <<EOF
{
  "query": "mutation CommentCreate(\$issueId: String!, \$body: String!) { commentCreate(input: { issueId: \$issueId, body: \$body }) { success comment { id body } } }",
  "variables": {
    "issueId": "${ISSUE_ID}",
    "body": "${COMMENT}"
  }
}
EOF

curl -X POST https://api.linear.app/graphql \
  -H "Authorization: ${LINEAR_API_TOKEN}" \
  -H "Content-Type: application/json" \
  -d @/tmp/linear-comment.json

Local

Update markdown file in

.aiwg/issues/
:

Read existing ticket:

TICKET_FILE=".aiwg/issues/${TICKET_ID}.md"

# Extract current metadata from frontmatter
CURRENT_STATUS=$(grep "^status:" "${TICKET_FILE}" | awk '{print $2}')
CURRENT_ASSIGNEE=$(grep "^assignee:" "${TICKET_FILE}" | awk '{print $2}')
CURRENT_LABELS=$(grep "^labels:" "${TICKET_FILE}" | cut -d':' -f2- | xargs)
CURRENT_PRIORITY=$(grep "^priority:" "${TICKET_FILE}" | awk '{print $2}')

Update metadata:

# Update status
if [ -n "${STATUS}" ]; then
  sed -i "s/^status:.*/status: ${STATUS}/" "${TICKET_FILE}"
  sed -i "/^\*\*Status\*\*:/s/:.*/: ${STATUS}/" "${TICKET_FILE}"
fi

# Update assignee
if [ -n "${ASSIGNEE}" ]; then
  sed -i "s/^assignee:.*/assignee: ${ASSIGNEE}/" "${TICKET_FILE}"
  sed -i "/^\*\*Assignee\*\*:/s/:.*/: ${ASSIGNEE}/" "${TICKET_FILE}"
fi

# Update priority
if [ -n "${PRIORITY}" ]; then
  sed -i "s/^priority:.*/priority: ${PRIORITY}/" "${TICKET_FILE}"
  sed -i "/^\*\*Priority\*\*:/s/:.*/: ${PRIORITY}/" "${TICKET_FILE}"
fi

# Update labels
if [ -n "${LABELS}" ]; then
  sed -i "s/^labels:.*/labels: ${LABELS}/" "${TICKET_FILE}"
fi

# Update timestamp
sed -i "s/^updated:.*/updated: $(date +%Y-%m-%d)/" "${TICKET_FILE}"

Add comment:

# Append comment to end of file
cat >> "${TICKET_FILE}" <<EOF

### $(date +%Y-%m-%d\ %H:%M)

${COMMENT}
EOF

Step 6: Return Confirmation

Output format (consistent across providers):

✅ Issue updated: {ticket-id}

{view-url-or-file-path}

**Updates**:
- Status: {old-status} → {new-status}
- Assignee: {old-assignee} → {new-assignee}
- Priority: {old-priority} → {new-priority}
- Labels: {old-labels} → {new-labels}
- Comment added: "{comment-preview...}"

## Next Steps

- View ticket: {url-or-command}
- Add another comment: `/issue-update {ticket-id} --comment "text"`
- Close ticket: `/issue-update {ticket-id} --status closed`
- List tickets: `/issue-list`

Examples

Example 1: Update Status (Gitea)

Command:

/issue-update ISSUE-042 --status in_progress --comment "Started implementation, created auth module"

Config (

.aiwg/config.yaml
):

ticketing:
  provider: gitea
  url: https://git.integrolabs.net
  owner: roctinam
  repo: ai-writing-guide

Output:

✅ Issue updated: ISSUE-042

View at: https://git.integrolabs.net/roctinam/ai-writing-guide/issues/42

**Updates**:
- Status: open → in_progress (Gitea: added 'in-progress' label)
- Comment added: "Started implementation, created auth module"

## Next Steps

- View ticket: https://git.integrolabs.net/roctinam/ai-writing-guide/issues/42
- Update status: `/issue-update ISSUE-042 --status closed`
- Add comment: `/issue-update ISSUE-042 --comment "Progress update"`

Example 2: Close Issue with Comment (Local)

Command:

/issue-update ISSUE-003 --status closed --comment "Fixed in commit abc123. Tested on iOS Safari 17."

Output:

✅ Issue updated: ISSUE-003

File: .aiwg/issues/ISSUE-003.md

**Updates**:
- Status: in_progress → closed
- Comment added: "Fixed in commit abc123. Tested on iOS Safari 17."

## Next Steps

- View ticket: cat .aiwg/issues/ISSUE-003.md
- Reopen if needed: `/issue-update ISSUE-003 --status open`
- List closed tickets: `/issue-list --status closed`

Example 3: Reassign Issue (GitHub)

Command:

/issue-update #128 --assignee security-team --priority critical --comment "Escalating to security team due to severity"

Config (

CLAUDE.md
):

## Issueing Configuration

- **Provider**: github
- **Owner**: jmagly
- **Repo**: ai-writing-guide

Output:

✅ Issue updated: #128

View at: https://github.com/jmagly/aiwg/issues/128

**Updates**:
- Assignee: unassigned → @security-team
- Priority: medium → critical (updated in issue body)
- Comment added: "Escalating to security team due to severity"

## Next Steps

- View ticket: gh issue view 128
- Track progress: `/issue-update 128 --comment "Status update"`
- Close when resolved: `/issue-update 128 --status closed`

Example 4: Add Labels Without Replacing (Gitea)

Command:

/issue-update ISSUE-042 --add-labels "urgent,needs-review" --comment "Ready for code review"

Output:

✅ Issue updated: ISSUE-042

View at: https://git.integrolabs.net/roctinam/ai-writing-guide/issues/42

**Updates**:
- Labels: feature, ui → feature, ui, urgent, needs-review
- Comment added: "Ready for code review"

## Next Steps

- View ticket: https://git.integrolabs.net/roctinam/ai-writing-guide/issues/42
- Remove label after review: `/issue-update ISSUE-042 --remove-labels "needs-review"`

Example 5: Multiple Field Updates (Local)

Command:

/issue-update ISSUE-005 --status blocked --assignee unassigned --priority high --labels "blocked,needs-discussion" --comment "Blocked on architecture decision ADR-003. Need team discussion."

Output:

✅ Issue updated: ISSUE-005

File: .aiwg/issues/ISSUE-005.md

**Updates**:
- Status: in_progress → blocked
- Assignee: johndoe → unassigned
- Priority: medium → high
- Labels: feature → blocked, needs-discussion
- Comment added: "Blocked on architecture decision ADR-003. Need team discussion."

## Next Steps

- View ticket: cat .aiwg/issues/ISSUE-005.md
- Unblock: `/issue-update ISSUE-005 --status in_progress --comment "Decision made, resuming work"`
- List blocked tickets: `/issue-list --status blocked`

Error Handling

Issue Not Found

❌ Issue not found: ISSUE-999

Searched in:
- Provider: gitea
- Repository: roctinam/ai-writing-guide

Available tickets:
- ISSUE-001: Implement user auth (open)
- ISSUE-002: Add dark mode (in_progress)
- ISSUE-003: Fix navigation bug (closed)

List all tickets: /issue-list
Create new ticket: /issue-create "title"

Invalid Status

❌ Invalid status: 'inprogress'

Valid statuses:
- open
- in_progress
- closed
- blocked
- review

Example: /issue-update ISSUE-001 --status in_progress

No Updates Specified

❌ No updates specified.

At least one update parameter required:
- --status STATE
- --comment "text"
- --assignee USER
- --labels "label1,label2"
- --add-labels "label1,label2"
- --remove-labels "label1,label2"
- --priority LEVEL

Example: /issue-update ISSUE-001 --status in_progress --comment "Started work"

Provider-Specific Errors

Gitea MCP Error:

❌ Failed to update Gitea issue:

Error: 403 Forbidden
- Insufficient permissions to edit issue
- Verify token permissions at https://git.integrolabs.net/user/settings/applications
- Token needs 'write:issue' scope

Cannot update ticket.

GitHub CLI Error:

❌ Failed to update GitHub issue:

Error: issue not found: #999
- Verify issue number: gh issue list --repo jmagly/ai-writing-guide
- Check repository access: gh auth status

Cannot update ticket.

Local File Error:

❌ Failed to update local ticket file:

Error: Permission denied - .aiwg/issues/ISSUE-001.md
- Check file permissions: ls -la .aiwg/issues/ISSUE-001.md
- Ensure writable: chmod 644 .aiwg/issues/ISSUE-001.md

Cannot update ticket.

Best Practices

  1. Add meaningful comments - Document progress, blockers, and decisions
  2. Update status frequently - Keep stakeholders informed
  3. Use status transitions wisely - Follow team workflow (e.g., open → in_progress → review → closed)
  4. Reassign when blocked - Don't let tickets languish without clear ownership
  5. Remove labels when resolved - Clean up metadata (e.g., remove "needs-review" after review)
  6. Close tickets with resolution comments - Document how issue was resolved
  7. Link to artifacts - Reference commits, PRs, or SDLC artifacts in comments
  8. Batch updates - Update multiple fields in single command when possible

Integration with SDLC Workflows

Construction Phase (Development):

# Start work
/issue-update ISSUE-001 --status in_progress --comment "Started implementation"

# Progress update
/issue-update ISSUE-001 --comment "Completed authentication module, 3 tests passing"

# Ready for review
/issue-update ISSUE-001 --add-labels "needs-review" --comment "Ready for code review, see PR #45"

# Close after merge
/issue-update ISSUE-001 --status closed --comment "Merged in PR #45, deployed to staging"

Testing Phase:

# Found bug during testing
/issue-update ISSUE-001 --status blocked --assignee qa-team --comment "Failed QA: auth broken on Safari"

# Bug fixed
/issue-update ISSUE-001 --status in_progress --assignee dev-team --comment "Fix deployed, ready for retest"

# Passed testing
/issue-update ISSUE-001 --status closed --comment "Passed QA, deployed to production"

Security Review:

# Start security review
/issue-update ISSUE-001 --status review --assignee security-team --add-labels "security-review"

# Findings identified
/issue-update ISSUE-001 --status blocked --comment "Security review found SQL injection risk, see ISSUE-042"

# Resolved
/issue-update ISSUE-001 --status closed --remove-labels "security-review" --comment "Security issues resolved, review passed"

Retrospectives:

# Action item from retro
/issue-update ISSUE-001 --add-labels "process-improvement" --comment "Retro action: Add automated tests for this pattern"

References

  • @$AIWG_ROOT/agentic/code/frameworks/sdlc-complete/config/issueing-config.md - Configuration schema
  • @$AIWG_ROOT/agentic/code/frameworks/sdlc-complete/commands/issue-create.md - Create ticket command
  • @$AIWG_ROOT/agentic/code/frameworks/sdlc-complete/commands/issue-list.md - List tickets command
  • @CLAUDE.md - User ticketing configuration