Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions .claude/skills/content-fix/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
---
name: content-fix
description: "Autonomous fixer for documentation content issues. Triggered by content_fix issues (documentation + fix labels) or @claude comments on those issues. Reads the issue, identifies affected files, applies fixes following Netwrix writing standards, and opens a PR. Asks clarifying questions if the request is ambiguous."
argument-hint: "[issue-number]"
---

# Content Fix

You are a documentation fixer that operates in GitHub Actions. A user has reported a documentation issue using the content_fix template. Your job is to understand what needs fixing, find the right file(s), apply the fix, and open a PR. If the request is ambiguous, ask a clarifying question on the issue and stop.

Read `docs/CLAUDE.md` before starting. It contains the Netwrix writing standards you must follow.

## Input

You receive:
- `$1`: The issue number
- `REPO`: The repository (owner/repo format)

Fetch the issue content:
```bash
ISSUE_BODY=$(gh issue view $1 --repo $REPO --json body --jq .body)
ISSUE_TITLE=$(gh issue view $1 --repo $REPO --json title --jq .title)
```

If this is a follow-up (triggered by an `@claude` comment), also read all comments:
```bash
COMMENTS=$(gh api repos/$REPO/issues/$1/comments --jq '.[].body')
```

## Step 1: Parse the issue

Extract the structured fields from the issue body. GitHub renders the content_fix template fields as H3 headers followed by content:
- **Issue type** — dropdown values like "The documentation is inaccurate", "The documentation is incomplete", etc.
- **Affected product and version** — dropdown values like "Auditor 10.8", "Access Analyzer 12.0", etc.
- **Issue** — the main description (required field)
- **Reproduction steps** — optional
- **Solution** — optional suggested fix

If this is a follow-up invocation, read all previous comments for additional context provided by the user.

## Step 2: Identify the file(s)

Use these strategies in priority order:

### Strategy A: URL extraction
If the issue description or comments contain a `docs.netwrix.com` URL, map it to a file path:
- URL path: `/docs/<product>/<version_with_underscores>/path/to/page`
- File path: `docs/<product>/<version_with_dots>/path/to/page.md` or `docs/<product>/<version_with_dots>/path/to/page/index.md`
- Version mapping: underscores in URL become dots in directory (e.g., `10_8` → `10.8`)
- SaaS products have no version segment: `/docs/1secure/path` → `docs/1secure/path.md`

Verify the mapped file exists before proceeding.

### Strategy B: Product + keyword search
If no URL is available but a product/version is specified:
1. Map the product name to a directory: "Auditor 10.8" → `docs/auditor/10.8/`, "Access Analyzer 12.0" → `docs/accessanalyzer/12.0/`
2. Search within that directory for keywords from the issue description using Grep
3. If multiple files match, use the issue context to narrow to the most likely file

### Strategy C: Ambiguous — ask and stop
If neither strategy yields a confident match:
```bash
gh issue comment $1 --repo $REPO --body "$(cat <<'EOF'
I'd like to help fix this, but I need a bit more information to find the right page.

Could you provide either:
- The URL of the page (e.g., `https://docs.netwrix.com/...`)
- The specific page title or section heading

Reply with `@claude` followed by the details and I'll get started.
EOF
)"
```
Then stop — do not edit any files.

## Step 3: Assess clarity

Before making edits, determine if the request is actionable:

**Clear enough to act on:**
- "The prerequisite list is missing .NET 4.8" — add the missing item
- "Step 3 says click Settings but the button is labeled Preferences" — fix the label
- "The page has broken links in the See Also section" — find and fix broken links
- "Grammar errors in the installation guide" — fix grammar issues

**Too vague — ask and stop:**
- "Something is wrong with the docs"
- "This page needs updating"
- "The information is incorrect" (without specifying what)

For vague requests, post a comment asking what specifically needs to change, then stop.

**Cannot handle — comment and stop:**
- "The screenshot is outdated" — post a comment noting that you cannot generate screenshots and a team member will need to provide the updated image, then stop
- Missing images — same approach

## Step 4: Post a progress comment

Post a comment on the issue with a checklist of what you're going to do:

```bash
PROGRESS_COMMENT_ID=$(gh issue comment $1 --repo $REPO --body "$(cat <<'EOF'
**Fix in progress:**

- [ ] Apply fix to `path/to/file.md`
- [ ] Run Vale and Dale
- [ ] Create PR
EOF
)" --format json | jq -r '.id' 2>/dev/null || echo "")
```

Tailor the checklist to the specific work. If fixing multiple files, list each one. If the issue mentions broken links, include "Fix broken links in `file.md`" as a line item.

Update the comment as you complete each item:
```bash
gh api repos/$REPO/issues/comments/$PROGRESS_COMMENT_ID \
-X PATCH -f body="<updated checklist with checked items>"
```

## Step 5: Make the fix

1. Create a branch:
```bash
git checkout -b fix/issue-$1-<short-slug>
```
Derive `<short-slug>` from the issue title: lowercase, replace non-alphanumeric with hyphens, truncate to 50 characters.

2. Read the file(s) and apply edits following `docs/CLAUDE.md` standards:
- Use the Edit tool for targeted changes
- Preserve the author's existing structure and intent — fix what the issue reported, don't rewrite unrelated content
- Only fix what the issue asks about

3. Run Vale on changed files:
```bash
vale <file>
```
Fix any Vale violations introduced by your edits.

4. Run Dale on changed files:
```bash
/dale <file>
```
Fix any Dale violations introduced by your edits.

## Step 6: Commit, push, and open PR

```bash
git add <changed-files>
git commit -m "docs: fix #$1 — <brief description>

<one-line summary of what was changed>

Co-Authored-By: Claude <noreply@anthropic.com>"
git push -u origin fix/issue-$1-<short-slug>
```

Open a PR targeting `dev`. Use an unquoted HEREDOC (`<<EOF`, not `<<'EOF'`) so that variables like `$1` are expanded:
```bash
ISSUE_NUM=$1
gh pr create --base dev --title "docs: fix #${ISSUE_NUM} — <brief description>" --body "$(cat <<EOF
Closes #${ISSUE_NUM}

## What changed
- <bullet points describing each fix>

## Files modified
- \`path/to/file.md\`

---
Automated fix for issue #${ISSUE_NUM}.
EOF
)"
```

## Step 7: Update progress comment

Replace the progress comment with a completion summary:

```bash
gh api repos/$REPO/issues/comments/$PROGRESS_COMMENT_ID \
-X PATCH -f body="$(cat <<'EOF'
**Fix complete:**

- [x] Apply fix to `path/to/file.md`
- [x] Run Vale and Dale
- [x] Create PR

**Summary:**
- `path/to/file.md`: <what was fixed>

PR: #<pr-number>
EOF
)"
```

## Failure handling

If you encounter an error mid-execution (Vale not available, edit conflict, push failure):

1. Post a comment on the issue explaining what went wrong and what was attempted
2. If a branch was created, note the branch name so a team member can continue manually
3. Do not leave the issue in a confusing state — always communicate what happened

## Behavioral notes

- **Confident = act, uncertain = ask.** If you can map a URL or find an exact file match, proceed. If the request is ambiguous, ask a clarifying question and stop.
- **Fix what's reported, nothing else.** If the issue says "fix the broken link in step 3," fix that link — don't also rewrite the sentence for clarity or fix grammar in other paragraphs.
- **If a fix would change the author's meaning**, skip it and note it in your summary. Ask how they'd like to handle it.
- **Each invocation is stateless.** You won't remember previous runs. Always read the full issue body and all comments for context.
- **Missing images cannot be fixed.** Note this in a comment and let a team member handle it.
101 changes: 101 additions & 0 deletions .github/workflows/claude-issue-labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,104 @@ jobs:
Note: The skill should preserve template-assigned labels and add complementary labels.
Issue body may have been modified by previous steps.
claude_args: '--allowedTools "Bash(gh:*),Skill(assign-label)"'

content-fix:
needs: process-issue
if: >-
github.event_name == 'issues' &&
github.event.action == 'opened' &&
contains(join(github.event.issue.labels.*.name, ','), 'documentation') &&
contains(join(github.event.issue.labels.*.name, ','), 'fix')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write

steps:
- name: Check if issue is still open
id: check-state
run: |
STATE=$(gh issue view ${{ github.event.issue.number }} --repo ${{ github.repository }} --json state --jq .state)
echo "issue_state=$STATE" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Checkout repository
if: steps.check-state.outputs.issue_state == 'OPEN'
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Run content-fix skill
if: steps.check-state.outputs.issue_state == 'OPEN'
uses: anthropics/claude-code-action@v1
env:
REPO: ${{ github.repository }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
show_full_output: true
prompt: |
/content-fix ${{ github.event.issue.number }}

Context:
- REPO: ${{ github.repository }}
- ISSUE_NUMBER: ${{ github.event.issue.number }}
- ISSUE_TITLE: ${{ github.event.issue.title }}
claude_args: '--allowedTools "Bash(gh:*),Bash(git:*),Bash(vale:*),Read,Write,Edit,Glob,Grep,Skill(content-fix),Skill(dale)"'

content-fix-followup:
needs: process-issue
if: >-
github.event_name == 'issue_comment' &&
!github.event.issue.pull_request &&
contains(github.event.comment.body, '@claude') &&
!startsWith(github.event.comment.user.login, 'github-actions') &&
contains(join(github.event.issue.labels.*.name, ','), 'documentation') &&
contains(join(github.event.issue.labels.*.name, ','), 'fix')
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
issues: write
id-token: write

steps:
- name: Check if issue is still open
id: check-state
run: |
STATE=$(gh issue view ${{ github.event.issue.number }} --repo ${{ github.repository }} --json state --jq .state)
echo "issue_state=$STATE" >> "$GITHUB_OUTPUT"
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Checkout repository
if: steps.check-state.outputs.issue_state == 'OPEN'
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Run content-fix skill
if: steps.check-state.outputs.issue_state == 'OPEN'
uses: anthropics/claude-code-action@v1
env:
REPO: ${{ github.repository }}
COMMENT_BODY: ${{ github.event.comment.body }}
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
show_full_output: true
prompt: |
/content-fix ${{ github.event.issue.number }}

Context:
- REPO: ${{ github.repository }}
- ISSUE_NUMBER: ${{ github.event.issue.number }}
- ISSUE_TITLE: ${{ github.event.issue.title }}
- TRIGGERING_COMMENT: $COMMENT_BODY

This is a follow-up invocation. A user replied with @claude on a content_fix issue.
Read the full issue body AND all comments to understand the complete context.
claude_args: '--allowedTools "Bash(gh:*),Bash(git:*),Bash(vale:*),Read,Write,Edit,Glob,Grep,Skill(content-fix),Skill(dale)"'
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ PRs target `dev`. Never commit directly to `dev` or `main`. The `sync-dev-to-mai
| `claude-doc-pr.yml` | PRs to dev with `docs/` changes | Dale + editorial review; `@claude` follow-up |
| `claude-documentation-reviewer.yml` | PRs with `.md` changes | AI review with inline suggestions |
| `claude-documentation-fixer.yml` | `@claude` comment on PR | Apply fixes and push |
| `claude-issue-labeler.yml` | Issues opened/edited | Security screening, CoC check, auto-labeling |
| `claude-issue-labeler.yml` | Issues opened/edited | Security screening, CoC check, auto-labeling, content fix automation |
| `sync-dev-to-main.yml` | Daily 8 AM PST | Auto-merge dev to main |
| `reindex-algolia.yml` | After main deploy | Refresh search index |

Expand All @@ -90,6 +90,7 @@ When a user asks for help with documentation, always use the appropriate tool:
| `/dale` | Skill | Custom linter for Netwrix-specific writing patterns |
| `/doc-help` | Skill | Interactive writing assistant (terminal sessions) |
| `/doc-pr` | Skill | Automated PR review (Dale + editorial) |
| `/content-fix` | Skill | Autonomous issue-to-PR fixer for content_fix issues |
| `/doc-pr-fix` | Skill | Autonomous PR fixer triggered by `@claude` |
| `tech-writer` | Agent | Autonomous end-to-end doc writing/editing |
| `vale-rule-writer` | Agent | Creates new Vale rules |
Expand Down
Loading