mirror of
https://github.com/anthropics/claude-code.git
synced 2026-06-15 11:03:28 +00:00
Compare commits
19 Commits
dalton/rea
...
inigo/clea
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d81efef324 | ||
|
|
c3a32e4ccf | ||
|
|
2511feadf3 | ||
|
|
bdba4a874d | ||
|
|
cd043128fe | ||
|
|
8fba17cb99 | ||
|
|
2b3a504f85 | ||
|
|
1bcc5cf5bd | ||
|
|
3e00a44590 | ||
|
|
702c601369 | ||
|
|
542b57b9a4 | ||
|
|
5c2a1e1d2e | ||
|
|
156d9e9e3f | ||
|
|
3404b075ec | ||
|
|
72042e95fb | ||
|
|
1feaffa747 | ||
|
|
7127181c79 | ||
|
|
e66b150c0e | ||
|
|
dbff8bea24 |
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
34
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,34 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: '[BUG] '
|
||||
labels: bug
|
||||
assignees: ''
|
||||
---
|
||||
|
||||
## Environment
|
||||
- Platform (select one):
|
||||
- [ ] Anthropic API
|
||||
- [ ] AWS Bedrock
|
||||
- [ ] Google Vertex AI
|
||||
- [ ] Other: <!-- specify -->
|
||||
- Claude CLI version: <!-- output of `claude --version` -->
|
||||
- Operating System: <!-- e.g. macOS 14.3, Windows 11, Ubuntu 22.04 -->
|
||||
- Terminal: <!-- e.g. iTerm2, Terminal App -->
|
||||
|
||||
## Bug Description
|
||||
<!-- A clear and concise description of the bug -->
|
||||
|
||||
## Steps to Reproduce
|
||||
1. <!-- First step -->
|
||||
2. <!-- Second step -->
|
||||
3. <!-- And so on... -->
|
||||
|
||||
## Expected Behavior
|
||||
<!-- What you expected to happen -->
|
||||
|
||||
## Actual Behavior
|
||||
<!-- What actually happened -->
|
||||
|
||||
## Additional Context
|
||||
<!-- Add any other context about the problem here, such as screenshots, logs, etc. -->
|
||||
188
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
188
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
name: 🐛 Bug Report
|
||||
description: Report a bug or unexpected behavior in Claude Code
|
||||
title: "[BUG] "
|
||||
labels:
|
||||
- bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to report this bug! Please fill out the sections below to help us understand and fix the issue.
|
||||
|
||||
Before submitting, please check:
|
||||
- You're using the [latest version](https://www.npmjs.com/package/@anthropic-ai/claude-code?activeTab=versions) of Claude Code (`claude --version`)
|
||||
- This issue hasn't already been reported by searching [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug).
|
||||
- This is a bug, not a feature request or support question
|
||||
|
||||
- type: checkboxes
|
||||
id: preflight
|
||||
attributes:
|
||||
label: Preflight Checklist
|
||||
description: Please confirm before submitting
|
||||
options:
|
||||
- label: I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Abug) and this hasn't been reported yet
|
||||
required: true
|
||||
- label: This is a single bug report (please file separate reports for different bugs)
|
||||
required: true
|
||||
- label: I am using the latest version of Claude Code
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: actual
|
||||
attributes:
|
||||
label: What's Wrong?
|
||||
description: Describe what's happening that shouldn't be
|
||||
placeholder: |
|
||||
When I try to create a Python file, Claude shows an error "EACCES: permission denied" and the file isn't created.
|
||||
|
||||
The command fails immediately after accepting the file write permission...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected
|
||||
attributes:
|
||||
label: What Should Happen?
|
||||
description: Describe the expected behavior
|
||||
placeholder: Claude should create a Python script file successfully without errors
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: error_output
|
||||
attributes:
|
||||
label: Error Messages/Logs
|
||||
description: If you see any error messages, paste them here
|
||||
placeholder: |
|
||||
Paste any error output, stack traces, or relevant logs here.
|
||||
This will be automatically formatted as code.
|
||||
render: shell
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: reproduction
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: |
|
||||
Please provide clear, numbered steps that anyone can follow to reproduce the issue.
|
||||
**Important**: Include any necessary code, file contents, or context needed to reproduce the bug.
|
||||
If the issue involves specific files or code, please create a minimal example.
|
||||
placeholder: |
|
||||
1. Create a file `test.py` with this content:
|
||||
```python
|
||||
def hello():
|
||||
print("test")
|
||||
```
|
||||
2. Run `claude "add type hints to test.py"`
|
||||
3. When prompted for file access, accept
|
||||
4. Error appears: "Unable to parse..."
|
||||
|
||||
Note: The bug only happens with Python files containing...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: model
|
||||
attributes:
|
||||
label: Claude Model
|
||||
description: Which model were you using? (Run `/model` to check)
|
||||
options:
|
||||
- Sonnet (default)
|
||||
- Opus
|
||||
- Not sure / Multiple models
|
||||
- Other
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: dropdown
|
||||
id: regression
|
||||
attributes:
|
||||
label: Is this a regression?
|
||||
description: Did this work in a previous version?
|
||||
options:
|
||||
- "Yes, this worked in a previous version"
|
||||
- "No, this never worked"
|
||||
- "I don't know"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: working_version
|
||||
attributes:
|
||||
label: Last Working Version
|
||||
description: If this is a regression, which version last worked? This helps expedite a fix.
|
||||
placeholder: "e.g., 1.0.100"
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Claude Code Version
|
||||
description: Run `claude --version` and paste the output
|
||||
placeholder: "e.g., 1.0.123 (Claude Code)"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Which API platform are you using?
|
||||
options:
|
||||
- Anthropic API
|
||||
- AWS Bedrock
|
||||
- Google Vertex AI
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: os
|
||||
attributes:
|
||||
label: Operating System
|
||||
options:
|
||||
- macOS
|
||||
- Windows
|
||||
- Ubuntu/Debian Linux
|
||||
- Other Linux
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: terminal
|
||||
attributes:
|
||||
label: Terminal/Shell
|
||||
description: Which terminal are you using?
|
||||
options:
|
||||
- Terminal.app (macOS)
|
||||
- Warp
|
||||
- Cursor
|
||||
- iTerm2
|
||||
- IntelliJ IDEA terminal
|
||||
- VS Code integrated terminal
|
||||
- PyCharm terminal
|
||||
- Windows Terminal
|
||||
- PowerShell
|
||||
- WSL (Windows Subsystem for Linux)
|
||||
- Xterm
|
||||
- Non-interactive/CI environment
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional Information
|
||||
description: |
|
||||
Anything else that might help us understand the issue?
|
||||
- Screenshots (drag and drop images here)
|
||||
- Configuration files
|
||||
- Related files or code
|
||||
- Links to repositories demonstrating the issue
|
||||
placeholder: Any additional context, screenshots, or information...
|
||||
validations:
|
||||
required: false
|
||||
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
17
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 💬 Discord Community
|
||||
url: https://anthropic.com/discord
|
||||
about: Get help, ask questions, and chat with other Claude Code users
|
||||
- name: 📖 Documentation
|
||||
url: https://docs.anthropic.com/en/docs/claude-code
|
||||
about: Read the official documentation and guides
|
||||
- name: 🎓 Getting Started Guide
|
||||
url: https://docs.anthropic.com/en/docs/claude-code/getting-started
|
||||
about: New to Claude Code? Start here
|
||||
- name: 🔧 Troubleshooting Guide
|
||||
url: https://docs.anthropic.com/en/docs/claude-code/troubleshooting
|
||||
about: Common issues and how to fix them
|
||||
- name: 💡 Discussions
|
||||
url: https://github.com/anthropics/claude-code/discussions
|
||||
about: Share ideas, tips, and chat with maintainers
|
||||
117
.github/ISSUE_TEMPLATE/documentation.yml
vendored
Normal file
117
.github/ISSUE_TEMPLATE/documentation.yml
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
name: 📚 Documentation Issue
|
||||
description: Report missing, unclear, or incorrect documentation
|
||||
title: "[DOCS] "
|
||||
labels:
|
||||
- documentation
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Help us improve our documentation!
|
||||
|
||||
Good documentation is crucial for a great developer experience. Please let us know what's missing or confusing.
|
||||
|
||||
- type: dropdown
|
||||
id: doc_type
|
||||
attributes:
|
||||
label: Documentation Type
|
||||
description: What kind of documentation issue is this?
|
||||
options:
|
||||
- Missing documentation (feature not documented)
|
||||
- Unclear/confusing documentation
|
||||
- Incorrect/outdated documentation
|
||||
- Typo or formatting issue
|
||||
- Missing code examples
|
||||
- Broken links
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: location
|
||||
attributes:
|
||||
label: Documentation Location
|
||||
description: Where did you encounter this issue? Provide a URL if possible
|
||||
placeholder: "e.g., https://docs.anthropic.com/en/docs/claude-code/getting-started"
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: input
|
||||
id: section
|
||||
attributes:
|
||||
label: Section/Topic
|
||||
description: Which specific section or topic needs improvement?
|
||||
placeholder: "e.g., MCP Server Configuration section"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: current
|
||||
attributes:
|
||||
label: Current Documentation
|
||||
description: |
|
||||
What does the documentation currently say?
|
||||
Quote the specific text if applicable.
|
||||
placeholder: |
|
||||
The docs currently say:
|
||||
"To configure MCP servers, add them to your configuration..."
|
||||
|
||||
But it doesn't explain...
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: issue
|
||||
attributes:
|
||||
label: What's Wrong or Missing?
|
||||
description: Explain what's incorrect, unclear, or missing
|
||||
placeholder: |
|
||||
The documentation doesn't explain how to:
|
||||
- Configure multiple MCP servers
|
||||
- Handle authentication
|
||||
- Debug connection issues
|
||||
|
||||
The example code doesn't work because...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: suggested
|
||||
attributes:
|
||||
label: Suggested Improvement
|
||||
description: How should the documentation be improved? Provide suggested text if possible
|
||||
placeholder: |
|
||||
The documentation should include:
|
||||
|
||||
1. A complete example showing...
|
||||
2. Explanation of common errors like...
|
||||
3. Step-by-step guide for...
|
||||
|
||||
Suggested text:
|
||||
"To configure multiple MCP servers, create an array in your settings..."
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: impact
|
||||
attributes:
|
||||
label: Impact
|
||||
description: How much does this documentation issue affect users?
|
||||
options:
|
||||
- High - Prevents users from using a feature
|
||||
- Medium - Makes feature difficult to understand
|
||||
- Low - Minor confusion or inconvenience
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: |
|
||||
- Screenshots showing the issue
|
||||
- Links to related documentation
|
||||
- Examples from other projects that do this well
|
||||
placeholder: Any additional information that would help...
|
||||
validations:
|
||||
required: false
|
||||
132
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
132
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
name: ✨ Feature Request
|
||||
description: Suggest a new feature or enhancement for Claude Code
|
||||
title: "[FEATURE] "
|
||||
labels:
|
||||
- enhancement
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Thanks for suggesting a feature!
|
||||
|
||||
We love hearing ideas from our community. Please help us understand your use case by filling out the sections below.
|
||||
Before submitting, please check if this feature has already been requested.
|
||||
|
||||
- type: checkboxes
|
||||
id: preflight
|
||||
attributes:
|
||||
label: Preflight Checklist
|
||||
options:
|
||||
- label: I have searched [existing requests](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20label%3Aenhancement) and this feature hasn't been requested yet
|
||||
required: true
|
||||
- label: This is a single feature request (not multiple features)
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: problem
|
||||
attributes:
|
||||
label: Problem Statement
|
||||
description: |
|
||||
What problem are you trying to solve? Why do you need this feature?
|
||||
Focus on the problem, not the solution. Help us understand your workflow.
|
||||
placeholder: |
|
||||
I often need to work with multiple projects simultaneously, but Claude Code doesn't support...
|
||||
|
||||
When I'm debugging code, I find it difficult to...
|
||||
|
||||
The current workflow requires me to manually...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: solution
|
||||
attributes:
|
||||
label: Proposed Solution
|
||||
description: |
|
||||
How would you like this to work? Describe the ideal user experience.
|
||||
Be specific about how you'd interact with this feature.
|
||||
placeholder: |
|
||||
I'd like to be able to run `claude --workspace project1,project2` to...
|
||||
|
||||
There should be a command or setting that allows...
|
||||
|
||||
The interface should show...
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: alternatives
|
||||
attributes:
|
||||
label: Alternative Solutions
|
||||
description: |
|
||||
What alternatives have you considered or tried?
|
||||
Are there workarounds you're currently using?
|
||||
placeholder: |
|
||||
I've tried using multiple terminal windows but...
|
||||
|
||||
Currently I work around this by...
|
||||
|
||||
Other tools solve this by...
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: dropdown
|
||||
id: priority
|
||||
attributes:
|
||||
label: Priority
|
||||
description: How important is this feature to your workflow?
|
||||
options:
|
||||
- Critical - Blocking my work
|
||||
- High - Significant impact on productivity
|
||||
- Medium - Would be very helpful
|
||||
- Low - Nice to have
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: category
|
||||
attributes:
|
||||
label: Feature Category
|
||||
description: What area does this feature relate to?
|
||||
options:
|
||||
- CLI commands and flags
|
||||
- Interactive mode (TUI)
|
||||
- File operations
|
||||
- API and model interactions
|
||||
- MCP server integration
|
||||
- Performance and speed
|
||||
- Configuration and settings
|
||||
- Developer tools/SDK
|
||||
- Documentation
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: use_case
|
||||
attributes:
|
||||
label: Use Case Example
|
||||
description: |
|
||||
Provide a concrete, real-world example of when you'd use this feature.
|
||||
Walk us through a scenario step-by-step.
|
||||
placeholder: |
|
||||
Example scenario:
|
||||
1. I'm working on a React app with a Node.js backend
|
||||
2. I need to make changes to both frontend and backend
|
||||
3. With this feature, I could...
|
||||
4. This would save me time because...
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: |
|
||||
- Screenshots or mockups of the proposed feature
|
||||
- Links to similar features in other tools
|
||||
- Technical considerations or constraints
|
||||
- Any other relevant information
|
||||
placeholder: Add any other context, mockups, or examples here...
|
||||
validations:
|
||||
required: false
|
||||
220
.github/ISSUE_TEMPLATE/model_behavior.yml
vendored
Normal file
220
.github/ISSUE_TEMPLATE/model_behavior.yml
vendored
Normal file
@@ -0,0 +1,220 @@
|
||||
name: 🤖 Model Behavior Issue
|
||||
description: Report unexpected Claude model behavior, incorrect actions, or permission violations
|
||||
title: "[MODEL] "
|
||||
labels:
|
||||
- model
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Report Unexpected Model Behavior
|
||||
|
||||
Use this template when Claude does something unexpected, makes unwanted changes, or behaves inconsistently with your instructions.
|
||||
|
||||
**This is for:** Unexpected actions, file modifications outside scope, ignoring instructions, making assumptions
|
||||
**NOT for:** Crashes, API errors, or installation issues (use Bug Report instead)
|
||||
|
||||
- type: checkboxes
|
||||
id: preflight
|
||||
attributes:
|
||||
label: Preflight Checklist
|
||||
description: Please confirm before submitting
|
||||
options:
|
||||
- label: I have searched [existing issues](https://github.com/anthropics/claude-code/issues?q=is%3Aissue%20state%3Aopen%20label%3Amodel) for similar behavior reports
|
||||
required: true
|
||||
- label: This report does NOT contain sensitive information (API keys, passwords, etc.)
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: behavior_type
|
||||
attributes:
|
||||
label: Type of Behavior Issue
|
||||
description: What category best describes the unexpected behavior?
|
||||
options:
|
||||
- Claude modified files I didn't ask it to modify
|
||||
- Claude accessed files outside the working directory
|
||||
- Claude ignored my instructions or configuration
|
||||
- Claude reverted/undid previous changes without asking
|
||||
- Claude made incorrect assumptions about my project
|
||||
- Claude refused a reasonable request
|
||||
- Claude's behavior changed between sessions
|
||||
- Subagent behaved unexpectedly
|
||||
- Other unexpected behavior
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: what_you_asked
|
||||
attributes:
|
||||
label: What You Asked Claude to Do
|
||||
description: Provide the exact prompt or command you gave
|
||||
placeholder: |
|
||||
I asked: "Update the README.md file to add installation instructions"
|
||||
|
||||
Or I ran: `claude "fix the bug in auth.js"`
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: what_claude_did
|
||||
attributes:
|
||||
label: What Claude Actually Did
|
||||
description: Describe step-by-step what Claude did instead
|
||||
placeholder: |
|
||||
1. Claude read README.md
|
||||
2. Instead of updating it, Claude deleted the entire file
|
||||
3. Created a new README from scratch with different content
|
||||
4. Also modified package.json without being asked
|
||||
5. Changed .gitignore file
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: expected_behavior
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: What should Claude have done?
|
||||
placeholder: |
|
||||
Claude should have:
|
||||
1. Read the existing README.md
|
||||
2. Added an "Installation" section
|
||||
3. Only modified that single file
|
||||
4. Not touched any other files
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: files_affected
|
||||
attributes:
|
||||
label: Files Affected
|
||||
description: |
|
||||
List all files that were accessed or modified (even if you didn't expect them to be)
|
||||
placeholder: |
|
||||
Modified:
|
||||
- README.md (deleted and recreated)
|
||||
- package.json (version bumped - not requested)
|
||||
- .gitignore (added entries - not requested)
|
||||
|
||||
Read (unexpectedly):
|
||||
- /Users/me/.ssh/config
|
||||
- ../../../parent-directory/secrets.env
|
||||
render: shell
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: dropdown
|
||||
id: permission_mode
|
||||
attributes:
|
||||
label: Permission Mode
|
||||
description: What permission settings were active?
|
||||
options:
|
||||
- Accept Edits was ON (auto-accepting changes)
|
||||
- Accept Edits was OFF (manual approval required)
|
||||
- I don't know / Not sure
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: reproducible
|
||||
attributes:
|
||||
label: Can You Reproduce This?
|
||||
description: Does this happen consistently?
|
||||
options:
|
||||
- Yes, every time with the same prompt
|
||||
- Sometimes (intermittent)
|
||||
- No, only happened once
|
||||
- Haven't tried to reproduce
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: reproduction_steps
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: If reproducible, provide minimal steps
|
||||
placeholder: |
|
||||
1. Create a new directory with a simple README.md
|
||||
2. Ask Claude Code to "improve the README"
|
||||
3. Claude will delete and recreate the file instead of editing
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: dropdown
|
||||
id: model
|
||||
attributes:
|
||||
label: Claude Model
|
||||
description: Which model were you using? (Run `/model` to check)
|
||||
options:
|
||||
- Sonnet
|
||||
- Opus
|
||||
- Haiku
|
||||
- Not sure
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: conversation_log
|
||||
attributes:
|
||||
label: Relevant Conversation
|
||||
description: |
|
||||
Include relevant parts of Claude's responses, especially where it explains what it's doing
|
||||
placeholder: |
|
||||
Claude said: "I'll help you update the README. Let me first delete the old one and create a fresh version..."
|
||||
|
||||
[Then proceeded to delete without asking for confirmation]
|
||||
render: markdown
|
||||
validations:
|
||||
required: false
|
||||
|
||||
- type: dropdown
|
||||
id: impact
|
||||
attributes:
|
||||
label: Impact
|
||||
description: How severe was the impact of this behavior?
|
||||
options:
|
||||
- Critical - Data loss or corrupted project
|
||||
- High - Significant unwanted changes
|
||||
- Medium - Extra work to undo changes
|
||||
- Low - Minor inconvenience
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Claude Code Version
|
||||
description: Run `claude --version` and paste the output
|
||||
placeholder: "e.g., 1.0.123 (Claude Code)"
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: dropdown
|
||||
id: platform
|
||||
attributes:
|
||||
label: Platform
|
||||
description: Which API platform are you using?
|
||||
options:
|
||||
- Anthropic API
|
||||
- AWS Bedrock
|
||||
- Google Vertex AI
|
||||
- Other
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
id: additional
|
||||
attributes:
|
||||
label: Additional Context
|
||||
description: |
|
||||
- Any patterns you've noticed
|
||||
- Similar behavior in other sessions
|
||||
- Specific file types or project structures that trigger this
|
||||
- Screenshots if relevant
|
||||
placeholder: |
|
||||
This seems to happen more often with:
|
||||
- Python projects
|
||||
- When there are multiple similar files
|
||||
- After long conversations
|
||||
validations:
|
||||
required: false
|
||||
31
.github/workflows/auto-close-duplicates.yml
vendored
31
.github/workflows/auto-close-duplicates.yml
vendored
@@ -1,31 +0,0 @@
|
||||
name: Auto-close duplicate issues
|
||||
description: Auto-closes issues that are duplicates of existing issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 9 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
auto-close-duplicates:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Auto-close duplicate issues
|
||||
run: bun run scripts/auto-close-duplicates.ts
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
|
||||
GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }}
|
||||
STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}
|
||||
@@ -1,44 +0,0 @@
|
||||
name: Backfill Duplicate Comments
|
||||
description: Triggers duplicate detection for old issues that don't have duplicate comments
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
days_back:
|
||||
description: 'How many days back to look for old issues'
|
||||
required: false
|
||||
default: '90'
|
||||
type: string
|
||||
dry_run:
|
||||
description: 'Dry run mode (true to only log what would be done)'
|
||||
required: false
|
||||
default: 'true'
|
||||
type: choice
|
||||
options:
|
||||
- 'true'
|
||||
- 'false'
|
||||
|
||||
jobs:
|
||||
backfill-duplicate-comments:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
permissions:
|
||||
contents: read
|
||||
issues: read
|
||||
actions: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Backfill duplicate comments
|
||||
run: bun run scripts/backfill-duplicate-comments.ts
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
DAYS_BACK: ${{ inputs.days_back }}
|
||||
DRY_RUN: ${{ inputs.dry_run }}
|
||||
80
.github/workflows/claude-dedupe-issues.yml
vendored
80
.github/workflows/claude-dedupe-issues.yml
vendored
@@ -1,80 +0,0 @@
|
||||
name: Claude Issue Dedupe
|
||||
description: Automatically dedupe GitHub issues using Claude Code
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: 'Issue number to process for duplicate detection'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
claude-dedupe-issues:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Run Claude Code slash command
|
||||
uses: anthropics/claude-code-base-action@beta
|
||||
with:
|
||||
prompt: "/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}"
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_env: |
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Log duplicate comment event to Statsig
|
||||
if: always()
|
||||
env:
|
||||
STATSIG_API_KEY: ${{ secrets.STATSIG_API_KEY }}
|
||||
run: |
|
||||
ISSUE_NUMBER=${{ github.event.issue.number || inputs.issue_number }}
|
||||
REPO=${{ github.repository }}
|
||||
|
||||
if [ -z "$STATSIG_API_KEY" ]; then
|
||||
echo "STATSIG_API_KEY not found, skipping Statsig logging"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Prepare the event payload
|
||||
EVENT_PAYLOAD=$(jq -n \
|
||||
--arg issue_number "$ISSUE_NUMBER" \
|
||||
--arg repo "$REPO" \
|
||||
--arg triggered_by "${{ github.event_name }}" \
|
||||
'{
|
||||
events: [{
|
||||
eventName: "github_duplicate_comment_added",
|
||||
value: 1,
|
||||
metadata: {
|
||||
repository: $repo,
|
||||
issue_number: ($issue_number | tonumber),
|
||||
triggered_by: $triggered_by,
|
||||
workflow_run_id: "${{ github.run_id }}"
|
||||
},
|
||||
time: (now | floor | tostring)
|
||||
}]
|
||||
}')
|
||||
|
||||
# Send to Statsig API
|
||||
echo "Logging duplicate comment event to Statsig for issue #${ISSUE_NUMBER}"
|
||||
|
||||
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST https://events.statsigapi.net/v1/log_event \
|
||||
-H "Content-Type: application/json" \
|
||||
-H "STATSIG-API-KEY: ${STATSIG_API_KEY}" \
|
||||
-d "$EVENT_PAYLOAD")
|
||||
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
BODY=$(echo "$RESPONSE" | head -n-1)
|
||||
|
||||
if [ "$HTTP_CODE" -eq 200 ] || [ "$HTTP_CODE" -eq 202 ]; then
|
||||
echo "Successfully logged duplicate comment event for issue #${ISSUE_NUMBER}"
|
||||
else
|
||||
echo "Failed to log duplicate comment event for issue #${ISSUE_NUMBER}. HTTP ${HTTP_CODE}: ${BODY}"
|
||||
fi
|
||||
59
.github/workflows/issue-notify.yml
vendored
59
.github/workflows/issue-notify.yml
vendored
@@ -1,59 +0,0 @@
|
||||
name: Issue Notification
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions:
|
||||
issues: read
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Send notification
|
||||
env:
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
ISSUE_TITLE: ${{ github.event.issue.title }}
|
||||
ISSUE_BODY: ${{ github.event.issue.body }}
|
||||
ISSUE_AUTHOR: ${{ github.event.issue.user.login }}
|
||||
ISSUE_LABELS: ${{ toJSON(github.event.issue.labels) }}
|
||||
ISSUE_URL: ${{ github.event.issue.html_url }}
|
||||
ISSUE_CREATED_AT: ${{ github.event.issue.created_at }}
|
||||
REPOSITORY: ${{ github.repository }}
|
||||
DISPATCH_TOKEN: ${{ secrets.CROSS_REPO_TOKEN_CLAUDE_CODE }}
|
||||
DISPATCH_ENDPOINT: ${{ secrets.DISPATCH_ENDPOINT }}
|
||||
run: |
|
||||
if [ -z "$DISPATCH_TOKEN" ] || [ -z "$DISPATCH_ENDPOINT" ]; then
|
||||
echo "Dispatch configuration not complete, skipping notification"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Prepare payload with issue metadata
|
||||
PAYLOAD=$(cat <<EOF
|
||||
{
|
||||
"event_type": "issue_event",
|
||||
"client_payload": {
|
||||
"source_repo": "$REPOSITORY",
|
||||
"issue_number": "$ISSUE_NUMBER",
|
||||
"issue_title": $(echo "$ISSUE_TITLE" | jq -Rs .),
|
||||
"issue_body": $(echo "$ISSUE_BODY" | jq -Rs .),
|
||||
"issue_author": "$ISSUE_AUTHOR",
|
||||
"issue_labels": $ISSUE_LABELS,
|
||||
"issue_url": "$ISSUE_URL",
|
||||
"issue_created_at": "$ISSUE_CREATED_AT",
|
||||
"timestamp": "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
)
|
||||
|
||||
# Send notification to configured endpoint
|
||||
curl -X POST \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-H "Authorization: token $DISPATCH_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
"$DISPATCH_ENDPOINT" \
|
||||
-d "$PAYLOAD" \
|
||||
--fail-with-body || echo "Notification sent"
|
||||
28
.github/workflows/issue-opened-dispatch.yml
vendored
Normal file
28
.github/workflows/issue-opened-dispatch.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
name: Issue Opened Dispatch
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions:
|
||||
issues: read
|
||||
actions: write
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 1
|
||||
steps:
|
||||
- name: Process new issue
|
||||
env:
|
||||
ISSUE_URL: ${{ github.event.issue.html_url }}
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
ISSUE_TITLE: ${{ github.event.issue.title }}
|
||||
TARGET_REPO: ${{ secrets.ISSUE_OPENED_DISPATCH_TARGET_REPO }}
|
||||
GH_TOKEN: ${{ secrets.ISSUE_OPENED_DISPATCH_TOKEN }}
|
||||
run: |
|
||||
gh api repos/${TARGET_REPO}/dispatches \
|
||||
-f event_type=issue_opened \
|
||||
-f client_payload[issue_url]="${ISSUE_URL}" || {
|
||||
exit 0
|
||||
}
|
||||
@@ -1,5 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.106
|
||||
|
||||
- Windows: Fixed path permission matching to consistently use POSIX format (e.g., `Read(//c/Users/...)`)
|
||||
|
||||
## 1.0.97
|
||||
|
||||
- Settings: /doctor now validates permission rule syntax and suggests corrections
|
||||
|
||||
@@ -1,277 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
declare global {
|
||||
var process: {
|
||||
env: Record<string, string | undefined>;
|
||||
};
|
||||
}
|
||||
|
||||
interface GitHubIssue {
|
||||
number: number;
|
||||
title: string;
|
||||
user: { id: number };
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
interface GitHubComment {
|
||||
id: number;
|
||||
body: string;
|
||||
created_at: string;
|
||||
user: { type: string; id: number };
|
||||
}
|
||||
|
||||
interface GitHubReaction {
|
||||
user: { id: number };
|
||||
content: string;
|
||||
}
|
||||
|
||||
async function githubRequest<T>(endpoint: string, token: string, method: string = 'GET', body?: any): Promise<T> {
|
||||
const response = await fetch(`https://api.github.com${endpoint}`, {
|
||||
method,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
Accept: "application/vnd.github.v3+json",
|
||||
"User-Agent": "auto-close-duplicates-script",
|
||||
...(body && { "Content-Type": "application/json" }),
|
||||
},
|
||||
...(body && { body: JSON.stringify(body) }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`GitHub API request failed: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
function extractDuplicateIssueNumber(commentBody: string): number | null {
|
||||
// Try to match #123 format first
|
||||
let match = commentBody.match(/#(\d+)/);
|
||||
if (match) {
|
||||
return parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
// Try to match GitHub issue URL format: https://github.com/owner/repo/issues/123
|
||||
match = commentBody.match(/github\.com\/[^\/]+\/[^\/]+\/issues\/(\d+)/);
|
||||
if (match) {
|
||||
return parseInt(match[1], 10);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
async function closeIssueAsDuplicate(
|
||||
owner: string,
|
||||
repo: string,
|
||||
issueNumber: number,
|
||||
duplicateOfNumber: number,
|
||||
token: string
|
||||
): Promise<void> {
|
||||
await githubRequest(
|
||||
`/repos/${owner}/${repo}/issues/${issueNumber}`,
|
||||
token,
|
||||
'PATCH',
|
||||
{
|
||||
state: 'closed',
|
||||
state_reason: 'duplicate',
|
||||
labels: ['duplicate']
|
||||
}
|
||||
);
|
||||
|
||||
await githubRequest(
|
||||
`/repos/${owner}/${repo}/issues/${issueNumber}/comments`,
|
||||
token,
|
||||
'POST',
|
||||
{
|
||||
body: `This issue has been automatically closed as a duplicate of #${duplicateOfNumber}.
|
||||
|
||||
If this is incorrect, please re-open this issue or create a new one.
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.ai/code)`
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
async function autoCloseDuplicates(): Promise<void> {
|
||||
console.log("[DEBUG] Starting auto-close duplicates script");
|
||||
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
if (!token) {
|
||||
throw new Error("GITHUB_TOKEN environment variable is required");
|
||||
}
|
||||
console.log("[DEBUG] GitHub token found");
|
||||
|
||||
const owner = process.env.GITHUB_REPOSITORY_OWNER || "anthropics";
|
||||
const repo = process.env.GITHUB_REPOSITORY_NAME || "claude-code";
|
||||
console.log(`[DEBUG] Repository: ${owner}/${repo}`);
|
||||
|
||||
const threeDaysAgo = new Date();
|
||||
threeDaysAgo.setDate(threeDaysAgo.getDate() - 3);
|
||||
console.log(
|
||||
`[DEBUG] Checking for duplicate comments older than: ${threeDaysAgo.toISOString()}`
|
||||
);
|
||||
|
||||
console.log("[DEBUG] Fetching open issues created more than 3 days ago...");
|
||||
const allIssues: GitHubIssue[] = [];
|
||||
let page = 1;
|
||||
const perPage = 100;
|
||||
|
||||
while (true) {
|
||||
const pageIssues: GitHubIssue[] = await githubRequest(
|
||||
`/repos/${owner}/${repo}/issues?state=open&per_page=${perPage}&page=${page}`,
|
||||
token
|
||||
);
|
||||
|
||||
if (pageIssues.length === 0) break;
|
||||
|
||||
// Filter for issues created more than 3 days ago
|
||||
const oldEnoughIssues = pageIssues.filter(issue =>
|
||||
new Date(issue.created_at) <= threeDaysAgo
|
||||
);
|
||||
|
||||
allIssues.push(...oldEnoughIssues);
|
||||
page++;
|
||||
|
||||
// Safety limit to avoid infinite loops
|
||||
if (page > 20) break;
|
||||
}
|
||||
|
||||
const issues = allIssues;
|
||||
console.log(`[DEBUG] Found ${issues.length} open issues`);
|
||||
|
||||
let processedCount = 0;
|
||||
let candidateCount = 0;
|
||||
|
||||
for (const issue of issues) {
|
||||
processedCount++;
|
||||
console.log(
|
||||
`[DEBUG] Processing issue #${issue.number} (${processedCount}/${issues.length}): ${issue.title}`
|
||||
);
|
||||
|
||||
console.log(`[DEBUG] Fetching comments for issue #${issue.number}...`);
|
||||
const comments: GitHubComment[] = await githubRequest(
|
||||
`/repos/${owner}/${repo}/issues/${issue.number}/comments`,
|
||||
token
|
||||
);
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} has ${comments.length} comments`
|
||||
);
|
||||
|
||||
const dupeComments = comments.filter(
|
||||
(comment) =>
|
||||
comment.body.includes("Found") &&
|
||||
comment.body.includes("possible duplicate") &&
|
||||
comment.user.type === "Bot"
|
||||
);
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} has ${dupeComments.length} duplicate detection comments`
|
||||
);
|
||||
|
||||
if (dupeComments.length === 0) {
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} - no duplicate comments found, skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const lastDupeComment = dupeComments[dupeComments.length - 1];
|
||||
const dupeCommentDate = new Date(lastDupeComment.created_at);
|
||||
console.log(
|
||||
`[DEBUG] Issue #${
|
||||
issue.number
|
||||
} - most recent duplicate comment from: ${dupeCommentDate.toISOString()}`
|
||||
);
|
||||
|
||||
if (dupeCommentDate > threeDaysAgo) {
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} - duplicate comment is too recent, skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
console.log(
|
||||
`[DEBUG] Issue #${
|
||||
issue.number
|
||||
} - duplicate comment is old enough (${Math.floor(
|
||||
(Date.now() - dupeCommentDate.getTime()) / (1000 * 60 * 60 * 24)
|
||||
)} days)`
|
||||
);
|
||||
|
||||
const commentsAfterDupe = comments.filter(
|
||||
(comment) => new Date(comment.created_at) > dupeCommentDate
|
||||
);
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} - ${commentsAfterDupe.length} comments after duplicate detection`
|
||||
);
|
||||
|
||||
if (commentsAfterDupe.length > 0) {
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} - has activity after duplicate comment, skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} - checking reactions on duplicate comment...`
|
||||
);
|
||||
const reactions: GitHubReaction[] = await githubRequest(
|
||||
`/repos/${owner}/${repo}/issues/comments/${lastDupeComment.id}/reactions`,
|
||||
token
|
||||
);
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} - duplicate comment has ${reactions.length} reactions`
|
||||
);
|
||||
|
||||
const authorThumbsDown = reactions.some(
|
||||
(reaction) =>
|
||||
reaction.user.id === issue.user.id && reaction.content === "-1"
|
||||
);
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} - author thumbs down reaction: ${authorThumbsDown}`
|
||||
);
|
||||
|
||||
if (authorThumbsDown) {
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} - author disagreed with duplicate detection, skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
const duplicateIssueNumber = extractDuplicateIssueNumber(lastDupeComment.body);
|
||||
if (!duplicateIssueNumber) {
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} - could not extract duplicate issue number from comment, skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
candidateCount++;
|
||||
const issueUrl = `https://github.com/${owner}/${repo}/issues/${issue.number}`;
|
||||
|
||||
try {
|
||||
console.log(
|
||||
`[INFO] Auto-closing issue #${issue.number} as duplicate of #${duplicateIssueNumber}: ${issueUrl}`
|
||||
);
|
||||
await closeIssueAsDuplicate(owner, repo, issue.number, duplicateIssueNumber, token);
|
||||
console.log(
|
||||
`[SUCCESS] Successfully closed issue #${issue.number} as duplicate of #${duplicateIssueNumber}`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[ERROR] Failed to close issue #${issue.number} as duplicate: ${error}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[DEBUG] Script completed. Processed ${processedCount} issues, found ${candidateCount} candidates for auto-close`
|
||||
);
|
||||
}
|
||||
|
||||
autoCloseDuplicates().catch(console.error);
|
||||
|
||||
// Make it a module
|
||||
export {};
|
||||
@@ -1,213 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
declare global {
|
||||
var process: {
|
||||
env: Record<string, string | undefined>;
|
||||
};
|
||||
}
|
||||
|
||||
interface GitHubIssue {
|
||||
number: number;
|
||||
title: string;
|
||||
state: string;
|
||||
state_reason?: string;
|
||||
user: { id: number };
|
||||
created_at: string;
|
||||
closed_at?: string;
|
||||
}
|
||||
|
||||
interface GitHubComment {
|
||||
id: number;
|
||||
body: string;
|
||||
created_at: string;
|
||||
user: { type: string; id: number };
|
||||
}
|
||||
|
||||
async function githubRequest<T>(endpoint: string, token: string, method: string = 'GET', body?: any): Promise<T> {
|
||||
const response = await fetch(`https://api.github.com${endpoint}`, {
|
||||
method,
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
Accept: "application/vnd.github.v3+json",
|
||||
"User-Agent": "backfill-duplicate-comments-script",
|
||||
...(body && { "Content-Type": "application/json" }),
|
||||
},
|
||||
...(body && { body: JSON.stringify(body) }),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(
|
||||
`GitHub API request failed: ${response.status} ${response.statusText}`
|
||||
);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
}
|
||||
|
||||
async function triggerDedupeWorkflow(
|
||||
owner: string,
|
||||
repo: string,
|
||||
issueNumber: number,
|
||||
token: string,
|
||||
dryRun: boolean = true
|
||||
): Promise<void> {
|
||||
if (dryRun) {
|
||||
console.log(`[DRY RUN] Would trigger dedupe workflow for issue #${issueNumber}`);
|
||||
return;
|
||||
}
|
||||
|
||||
await githubRequest(
|
||||
`/repos/${owner}/${repo}/actions/workflows/claude-dedupe-issues.yml/dispatches`,
|
||||
token,
|
||||
'POST',
|
||||
{
|
||||
ref: 'main',
|
||||
inputs: {
|
||||
issue_number: issueNumber.toString()
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async function backfillDuplicateComments(): Promise<void> {
|
||||
console.log("[DEBUG] Starting backfill duplicate comments script");
|
||||
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
if (!token) {
|
||||
throw new Error(`GITHUB_TOKEN environment variable is required
|
||||
|
||||
Usage:
|
||||
GITHUB_TOKEN=your_token bun run scripts/backfill-duplicate-comments.ts
|
||||
|
||||
Environment Variables:
|
||||
GITHUB_TOKEN - GitHub personal access token with repo and actions permissions (required)
|
||||
DRY_RUN - Set to "false" to actually trigger workflows (default: true for safety)
|
||||
MAX_ISSUE_NUMBER - Only process issues with numbers less than this value (default: 4050)`);
|
||||
}
|
||||
console.log("[DEBUG] GitHub token found");
|
||||
|
||||
const owner = "anthropics";
|
||||
const repo = "claude-code";
|
||||
const dryRun = process.env.DRY_RUN !== "false";
|
||||
const maxIssueNumber = parseInt(process.env.MAX_ISSUE_NUMBER || "4050", 10);
|
||||
const minIssueNumber = parseInt(process.env.MIN_ISSUE_NUMBER || "1", 10);
|
||||
|
||||
console.log(`[DEBUG] Repository: ${owner}/${repo}`);
|
||||
console.log(`[DEBUG] Dry run mode: ${dryRun}`);
|
||||
console.log(`[DEBUG] Looking at issues between #${minIssueNumber} and #${maxIssueNumber}`);
|
||||
|
||||
console.log(`[DEBUG] Fetching issues between #${minIssueNumber} and #${maxIssueNumber}...`);
|
||||
const allIssues: GitHubIssue[] = [];
|
||||
let page = 1;
|
||||
const perPage = 100;
|
||||
|
||||
while (true) {
|
||||
const pageIssues: GitHubIssue[] = await githubRequest(
|
||||
`/repos/${owner}/${repo}/issues?state=all&per_page=${perPage}&page=${page}&sort=created&direction=desc`,
|
||||
token
|
||||
);
|
||||
|
||||
if (pageIssues.length === 0) break;
|
||||
|
||||
// Filter to only include issues within the specified range
|
||||
const filteredIssues = pageIssues.filter(issue =>
|
||||
issue.number >= minIssueNumber && issue.number < maxIssueNumber
|
||||
);
|
||||
allIssues.push(...filteredIssues);
|
||||
|
||||
// If the oldest issue in this page is still above our minimum, we need to continue
|
||||
// but if the oldest issue is below our minimum, we can stop
|
||||
const oldestIssueInPage = pageIssues[pageIssues.length - 1];
|
||||
if (oldestIssueInPage && oldestIssueInPage.number >= maxIssueNumber) {
|
||||
console.log(`[DEBUG] Oldest issue in page #${page} is #${oldestIssueInPage.number}, continuing...`);
|
||||
} else if (oldestIssueInPage && oldestIssueInPage.number < minIssueNumber) {
|
||||
console.log(`[DEBUG] Oldest issue in page #${page} is #${oldestIssueInPage.number}, below minimum, stopping`);
|
||||
break;
|
||||
} else if (filteredIssues.length === 0 && pageIssues.length > 0) {
|
||||
console.log(`[DEBUG] No issues in page #${page} are in range #${minIssueNumber}-#${maxIssueNumber}, continuing...`);
|
||||
}
|
||||
|
||||
page++;
|
||||
|
||||
// Safety limit to avoid infinite loops
|
||||
if (page > 200) {
|
||||
console.log("[DEBUG] Reached page limit, stopping pagination");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[DEBUG] Found ${allIssues.length} issues between #${minIssueNumber} and #${maxIssueNumber}`);
|
||||
|
||||
let processedCount = 0;
|
||||
let candidateCount = 0;
|
||||
let triggeredCount = 0;
|
||||
|
||||
for (const issue of allIssues) {
|
||||
processedCount++;
|
||||
console.log(
|
||||
`[DEBUG] Processing issue #${issue.number} (${processedCount}/${allIssues.length}): ${issue.title}`
|
||||
);
|
||||
|
||||
console.log(`[DEBUG] Fetching comments for issue #${issue.number}...`);
|
||||
const comments: GitHubComment[] = await githubRequest(
|
||||
`/repos/${owner}/${repo}/issues/${issue.number}/comments`,
|
||||
token
|
||||
);
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} has ${comments.length} comments`
|
||||
);
|
||||
|
||||
// Look for existing duplicate detection comments (from the dedupe bot)
|
||||
const dupeDetectionComments = comments.filter(
|
||||
(comment) =>
|
||||
comment.body.includes("Found") &&
|
||||
comment.body.includes("possible duplicate") &&
|
||||
comment.user.type === "Bot"
|
||||
);
|
||||
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} has ${dupeDetectionComments.length} duplicate detection comments`
|
||||
);
|
||||
|
||||
// Skip if there's already a duplicate detection comment
|
||||
if (dupeDetectionComments.length > 0) {
|
||||
console.log(
|
||||
`[DEBUG] Issue #${issue.number} already has duplicate detection comment, skipping`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
candidateCount++;
|
||||
const issueUrl = `https://github.com/${owner}/${repo}/issues/${issue.number}`;
|
||||
|
||||
try {
|
||||
console.log(
|
||||
`[INFO] ${dryRun ? '[DRY RUN] ' : ''}Triggering dedupe workflow for issue #${issue.number}: ${issueUrl}`
|
||||
);
|
||||
await triggerDedupeWorkflow(owner, repo, issue.number, token, dryRun);
|
||||
|
||||
if (!dryRun) {
|
||||
console.log(
|
||||
`[SUCCESS] Successfully triggered dedupe workflow for issue #${issue.number}`
|
||||
);
|
||||
}
|
||||
triggeredCount++;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`[ERROR] Failed to trigger workflow for issue #${issue.number}: ${error}`
|
||||
);
|
||||
}
|
||||
|
||||
// Add a delay between workflow triggers to avoid overwhelming the system
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[DEBUG] Script completed. Processed ${processedCount} issues, found ${candidateCount} candidates without duplicate comments, ${dryRun ? 'would trigger' : 'triggered'} ${triggeredCount} workflows`
|
||||
);
|
||||
}
|
||||
|
||||
backfillDuplicateComments().catch(console.error);
|
||||
|
||||
// Make it a module
|
||||
export {};
|
||||
Reference in New Issue
Block a user