mirror of
https://github.com/anthropics/claude-code.git
synced 2026-04-17 08:42:45 +00:00
Compare commits
1 Commits
v2.1.71
...
claude/sla
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
965dbf27eb |
@@ -1,5 +1,5 @@
|
||||
---
|
||||
allowed-tools: Bash(./scripts/gh.sh:*), Bash(./scripts/comment-on-duplicates.sh:*)
|
||||
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(./scripts/comment-on-duplicates.sh:*)
|
||||
description: Find duplicate GitHub issues
|
||||
---
|
||||
|
||||
@@ -18,10 +18,6 @@ To do this, follow these steps precisely:
|
||||
|
||||
Notes (be sure to tell this to your agents, too):
|
||||
|
||||
- Use `./scripts/gh.sh` to interact with Github, rather than web fetch or raw `gh`. Examples:
|
||||
- `./scripts/gh.sh issue view 123` — view an issue
|
||||
- `./scripts/gh.sh issue view 123 --comments` — view with comments
|
||||
- `./scripts/gh.sh issue list --state open --limit 20` — list issues
|
||||
- `./scripts/gh.sh search issues "query" --limit 10` — search for issues
|
||||
- Do not use other tools, beyond `./scripts/gh.sh` and the comment script (eg. don't use other MCP servers, file edit, etc.)
|
||||
- Use `gh` to interact with Github, rather than web fetch
|
||||
- Do not use other tools, beyond `gh` and the comment script (eg. don't use other MCP servers, file edit, etc.)
|
||||
- Make a todo list first
|
||||
|
||||
40
.claude/commands/oncall-triage.md
Normal file
40
.claude/commands/oncall-triage.md
Normal file
@@ -0,0 +1,40 @@
|
||||
---
|
||||
allowed-tools: Bash(gh issue list:*), Bash(gh issue view:*), Bash(gh issue edit:*), TodoWrite
|
||||
description: Triage GitHub issues and label critical ones for oncall
|
||||
---
|
||||
|
||||
You're an oncall triage assistant for GitHub issues. Your task is to identify critical issues that require immediate oncall attention and apply the "oncall" label.
|
||||
|
||||
Repository: anthropics/claude-code
|
||||
|
||||
Task overview:
|
||||
|
||||
1. First, get all open bugs updated in the last 3 days with at least 50 engagements:
|
||||
```bash
|
||||
gh issue list --repo anthropics/claude-code --state open --label bug --limit 1000 --json number,title,updatedAt,comments,reactions | jq -r '.[] | select((.updatedAt >= (now - 259200 | strftime("%Y-%m-%dT%H:%M:%SZ"))) and ((.comments | length) + ([.reactions[].content] | length) >= 50)) | "\(.number)"'
|
||||
```
|
||||
|
||||
2. Save the list of issue numbers and create a TODO list with ALL of them. This ensures you process every single one.
|
||||
|
||||
3. For each issue in your TODO list:
|
||||
- Use `gh issue view <number> --repo anthropics/claude-code --json title,body,labels,comments` to get full details
|
||||
- Read and understand the full issue content and comments to determine actual user impact
|
||||
- Evaluate: Is this truly blocking users from using Claude Code?
|
||||
- Consider: "crash", "stuck", "frozen", "hang", "unresponsive", "cannot use", "blocked", "broken"
|
||||
- Does it prevent core functionality? Can users work around it?
|
||||
- Be conservative - only flag issues that truly prevent users from getting work done
|
||||
|
||||
4. For issues that are truly blocking and don't already have the "oncall" label:
|
||||
- Use `gh issue edit <number> --repo anthropics/claude-code --add-label "oncall"`
|
||||
- Mark the issue as complete in your TODO list
|
||||
|
||||
5. After processing all issues, provide a summary:
|
||||
- List each issue number that received the "oncall" label
|
||||
- Include the issue title and brief reason why it qualified
|
||||
- If no issues qualified, state that clearly
|
||||
|
||||
Important:
|
||||
- Process ALL issues in your TODO list systematically
|
||||
- Don't post any comments to issues
|
||||
- Only add the "oncall" label, never remove it
|
||||
- Use individual `gh issue view` commands instead of bash for loops to avoid approval prompts
|
||||
@@ -1,70 +0,0 @@
|
||||
---
|
||||
allowed-tools: Bash(./scripts/gh.sh:*),Bash(./scripts/edit-issue-labels.sh:*)
|
||||
description: Triage GitHub issues by analyzing and applying labels
|
||||
---
|
||||
|
||||
You're an issue triage assistant. Analyze the issue and manage labels.
|
||||
|
||||
IMPORTANT: Don't post any comments or messages to the issue. Your only actions are adding or removing labels.
|
||||
|
||||
Context:
|
||||
|
||||
$ARGUMENTS
|
||||
|
||||
TOOLS:
|
||||
- `./scripts/gh.sh` — wrapper for `gh` CLI. Only supports these subcommands and flags:
|
||||
- `./scripts/gh.sh label list` — fetch all available labels
|
||||
- `./scripts/gh.sh label list --limit 100` — fetch with limit
|
||||
- `./scripts/gh.sh issue view 123` — read issue title, body, and labels
|
||||
- `./scripts/gh.sh issue view 123 --comments` — read the conversation
|
||||
- `./scripts/gh.sh issue list --state open --limit 20` — list issues
|
||||
- `./scripts/gh.sh search issues "query"` — find similar or duplicate issues
|
||||
- `./scripts/gh.sh search issues "query" --limit 10` — search with limit
|
||||
- `./scripts/edit-issue-labels.sh --issue NUMBER --add-label LABEL --remove-label LABEL` — add or remove labels
|
||||
|
||||
TASK:
|
||||
|
||||
1. Run `./scripts/gh.sh label list` to fetch the available labels. You may ONLY use labels from this list. Never invent new labels.
|
||||
2. Run `./scripts/gh.sh issue view ISSUE_NUMBER` to read the issue details.
|
||||
3. Run `./scripts/gh.sh issue view ISSUE_NUMBER --comments` to read the conversation.
|
||||
|
||||
**If EVENT is "issues" (new issue):**
|
||||
|
||||
4. First, check if this issue is actually about Claude Code (the CLI/IDE tool). Issues about the Claude API, claude.ai, the Claude app, Anthropic billing, or other Anthropic products should be labeled `invalid`. If invalid, apply only that label and stop.
|
||||
|
||||
5. Analyze and apply category labels:
|
||||
- Type (bug, enhancement, question, etc.)
|
||||
- Technical areas and platform
|
||||
- Check for duplicates with `./scripts/gh.sh search issues`. Only mark as duplicate of OPEN issues.
|
||||
|
||||
6. Evaluate lifecycle labels:
|
||||
- `needs-repro` (bugs only, 7 days): Bug reports without clear steps to reproduce. A good repro has specific, followable steps that someone else could use to see the same issue.
|
||||
Do NOT apply if the user already provided error messages, logs, file paths, or a description of what they did. Don't require a specific format — narrative descriptions count.
|
||||
For model behavior issues (e.g. "Claude does X when it should do Y"), don't require traditional repro steps — examples and patterns are sufficient.
|
||||
- `needs-info` (bugs only, 7 days): The issue needs something from the community before it can progress — e.g. error messages, versions, environment details, or answers to follow-up questions. Don't apply to questions or enhancements.
|
||||
Do NOT apply if the user already provided version, environment, and error details. If the issue just needs engineering investigation, that's not `needs-info`.
|
||||
|
||||
Issues with these labels are automatically closed after the timeout if there's no response.
|
||||
The goal is to avoid issues lingering without a clear next step.
|
||||
|
||||
7. Apply all selected labels:
|
||||
`./scripts/edit-issue-labels.sh --issue ISSUE_NUMBER --add-label "label1" --add-label "label2"`
|
||||
|
||||
**If EVENT is "issue_comment" (comment on existing issue):**
|
||||
|
||||
4. Evaluate lifecycle labels based on the full conversation:
|
||||
- If the issue has `stale` or `autoclose`, remove the label — a new human comment means the issue is still active:
|
||||
`./scripts/edit-issue-labels.sh --issue ISSUE_NUMBER --remove-label "stale" --remove-label "autoclose"`
|
||||
- If the issue has `needs-repro` or `needs-info` and the missing information has now been provided, remove the label:
|
||||
`./scripts/edit-issue-labels.sh --issue ISSUE_NUMBER --remove-label "needs-repro"`
|
||||
- If the issue doesn't have lifecycle labels but clearly needs them (e.g., a maintainer asked for repro steps or more details), add the appropriate label.
|
||||
- Comments like "+1", "me too", "same here", or emoji reactions are NOT the missing information. Only remove `needs-repro` or `needs-info` when substantive details are actually provided.
|
||||
- Do NOT add or remove category labels (bug, enhancement, etc.) on comment events.
|
||||
|
||||
GUIDELINES:
|
||||
- ONLY use labels from `./scripts/gh.sh label list` — never create or guess label names
|
||||
- DO NOT post any comments to the issue
|
||||
- Be conservative with lifecycle labels — only apply when clearly warranted
|
||||
- Only apply lifecycle labels (`needs-repro`, `needs-info`) to bugs — never to questions or enhancements
|
||||
- When in doubt, don't apply a lifecycle label — false positives are worse than missing labels
|
||||
- It's okay to not add any labels if none are clearly applicable
|
||||
1
.github/workflows/claude-dedupe-issues.yml
vendored
1
.github/workflows/claude-dedupe-issues.yml
vendored
@@ -17,6 +17,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
70
.github/workflows/claude-issue-triage.yml
vendored
70
.github/workflows/claude-issue-triage.yml
vendored
@@ -18,6 +18,7 @@ jobs:
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
@@ -28,11 +29,76 @@ jobs:
|
||||
uses: anthropics/claude-code-action@v1
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GH_REPO: ${{ github.repository }}
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allowed_non_write_users: "*"
|
||||
prompt: "/triage-issue REPO: ${{ github.repository }} ISSUE_NUMBER: ${{ github.event.issue.number }} EVENT: ${{ github.event_name }}"
|
||||
prompt: |
|
||||
You're an issue triage assistant. Analyze the issue and manage labels.
|
||||
|
||||
IMPORTANT: Don't post any comments or messages to the issue. Your only actions are adding or removing labels.
|
||||
|
||||
Context:
|
||||
- REPO: ${{ github.repository }}
|
||||
- ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
- EVENT: ${{ github.event_name }}
|
||||
|
||||
ALLOWED LABELS — you may ONLY use labels from this list. Never invent new labels.
|
||||
|
||||
Type: bug, enhancement, question, documentation, duplicate, invalid
|
||||
Lifecycle: needs-repro, needs-info
|
||||
Platform: platform:linux, platform:macos, platform:windows, platform:wsl, platform:ios, platform:android, platform:vscode, platform:intellij, platform:web, platform:aws-bedrock
|
||||
API: api:bedrock, api:vertex
|
||||
|
||||
TOOLS:
|
||||
- `gh issue view NUMBER`: Read the issue title, body, and labels
|
||||
- `gh issue view NUMBER --comments`: Read the conversation
|
||||
- `gh search issues QUERY`: Find similar or duplicate issues
|
||||
- `gh issue edit NUMBER --add-label` / `--remove-label`: Add or remove labels
|
||||
|
||||
TASK:
|
||||
|
||||
1. Run `gh issue view ${{ github.event.issue.number }}` to read the issue details.
|
||||
2. Run `gh issue view ${{ github.event.issue.number }} --comments` to read the conversation.
|
||||
|
||||
**If EVENT is "issues" (new issue):**
|
||||
|
||||
3. First, check if this issue is actually about Claude Code (the CLI/IDE tool). Issues about the Claude API, claude.ai, the Claude app, Anthropic billing, or other Anthropic products should be labeled `invalid`. If invalid, apply only that label and stop.
|
||||
|
||||
4. Analyze and apply category labels:
|
||||
- Type (bug, enhancement, question, etc.)
|
||||
- Technical areas and platform
|
||||
- Check for duplicates with `gh search issues`. Only mark as duplicate of OPEN issues.
|
||||
|
||||
5. Evaluate lifecycle labels:
|
||||
- `needs-repro` (bugs only, 7 days): Bug reports without clear steps to reproduce. A good repro has specific, followable steps that someone else could use to see the same issue.
|
||||
Do NOT apply if the user already provided error messages, logs, file paths, or a description of what they did. Don't require a specific format — narrative descriptions count.
|
||||
For model behavior issues (e.g. "Claude does X when it should do Y"), don't require traditional repro steps — examples and patterns are sufficient.
|
||||
- `needs-info` (bugs only, 7 days): The issue needs something from the community before it can progress — e.g. error messages, versions, environment details, or answers to follow-up questions. Don't apply to questions or enhancements.
|
||||
Do NOT apply if the user already provided version, environment, and error details. If the issue just needs engineering investigation, that's not `needs-info`.
|
||||
|
||||
Issues with these labels are automatically closed after the timeout if there's no response.
|
||||
The goal is to avoid issues lingering without a clear next step.
|
||||
|
||||
6. Apply all selected labels:
|
||||
`gh issue edit ${{ github.event.issue.number }} --add-label "label1" --add-label "label2"`
|
||||
|
||||
**If EVENT is "issue_comment" (comment on existing issue):**
|
||||
|
||||
3. Evaluate lifecycle labels based on the full conversation:
|
||||
- If the issue has `needs-repro` or `needs-info` and the missing information has now been provided, remove the label:
|
||||
`gh issue edit ${{ github.event.issue.number }} --remove-label "needs-repro"`
|
||||
- If the issue doesn't have lifecycle labels but clearly needs them (e.g., a maintainer asked for repro steps or more details), add the appropriate label.
|
||||
- Comments like "+1", "me too", "same here", or emoji reactions are NOT the missing information. Only remove labels when substantive details are actually provided.
|
||||
- Do NOT add or remove category labels (bug, enhancement, etc.) on comment events.
|
||||
|
||||
GUIDELINES:
|
||||
- ONLY use labels from the ALLOWED LABELS list above — never create or guess label names
|
||||
- DO NOT post any comments to the issue
|
||||
- Be conservative with lifecycle labels — only apply when clearly warranted
|
||||
- Only apply lifecycle labels (`needs-repro`, `needs-info`) to bugs — never to questions or enhancements
|
||||
- When in doubt, don't apply a lifecycle label — false positives are worse than missing labels
|
||||
- It's okay to not add any labels if none are clearly applicable
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_args: |
|
||||
--model claude-opus-4-6
|
||||
--allowedTools "Bash(gh issue view:*),Bash(gh issue edit:*),Bash(gh search issues:*)"
|
||||
|
||||
27
.github/workflows/issue-lifecycle-comment.yml
vendored
27
.github/workflows/issue-lifecycle-comment.yml
vendored
@@ -1,27 +0,0 @@
|
||||
name: "Issue Lifecycle Comment"
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [labeled]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: latest
|
||||
|
||||
- name: Post lifecycle comment
|
||||
run: bun run scripts/lifecycle-comment.ts
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
LABEL: ${{ github.event.label.name }}
|
||||
ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
47
.github/workflows/non-write-users-check.yml
vendored
47
.github/workflows/non-write-users-check.yml
vendored
@@ -1,47 +0,0 @@
|
||||
name: Non-write Users Check
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/**"
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
allowed-non-write-check:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
steps:
|
||||
- run: |
|
||||
DIFF=$(gh pr diff "$PR_NUMBER" -R "$REPO" || true)
|
||||
|
||||
if ! echo "$DIFF" | grep -qE '^diff --git a/\.github/.*\.ya?ml'; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
MATCHES=$(echo "$DIFF" | grep "^+.*allowed_non_write_users" || true)
|
||||
|
||||
if [ -z "$MATCHES" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
EXISTING=$(gh pr view "$PR_NUMBER" -R "$REPO" --json comments --jq '.comments[].body' \
|
||||
| grep -c "<!-- non-write-users-check -->" || true)
|
||||
|
||||
if [ "$EXISTING" -gt 0 ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
gh pr comment "$PR_NUMBER" -R "$REPO" --body '<!-- non-write-users-check -->
|
||||
**`allowed_non_write_users` detected**
|
||||
|
||||
This PR adds or modifies `allowed_non_write_users`, which allows users without write access to trigger Claude Code Action workflows. This can introduce security risks.
|
||||
|
||||
If this is a new flow, please make sure you actually need `allowed_non_write_users`. If you are editing an existing workflow, double check that you are not adding new Claude permissions which might lead to a vulnerability.
|
||||
|
||||
See existing workflows in this repo for safe usage examples, or contact the AppSec team.'
|
||||
env:
|
||||
PR_NUMBER: ${{ github.event.pull_request.number }}
|
||||
REPO: ${{ github.repository }}
|
||||
118
.github/workflows/oncall-triage.yml
vendored
Normal file
118
.github/workflows/oncall-triage.yml
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
name: Oncall Issue Triage
|
||||
description: Automatically identify and label critical blocking issues requiring oncall attention
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- add-oncall-triage-workflow # Temporary: for testing only
|
||||
schedule:
|
||||
# Run every 6 hours
|
||||
- cron: '0 */6 * * *'
|
||||
workflow_dispatch: # Allow manual trigger
|
||||
|
||||
jobs:
|
||||
oncall-triage:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
permissions:
|
||||
contents: read
|
||||
issues: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup GitHub MCP Server
|
||||
run: |
|
||||
mkdir -p /tmp/mcp-config
|
||||
cat > /tmp/mcp-config/mcp-servers.json << 'EOF'
|
||||
{
|
||||
"mcpServers": {
|
||||
"github": {
|
||||
"command": "docker",
|
||||
"args": [
|
||||
"run",
|
||||
"-i",
|
||||
"--rm",
|
||||
"-e",
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN",
|
||||
"ghcr.io/github/github-mcp-server:sha-7aced2b"
|
||||
],
|
||||
"env": {
|
||||
"GITHUB_PERSONAL_ACCESS_TOKEN": "${{ secrets.GITHUB_TOKEN }}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
- name: Run Claude Code for Oncall Triage
|
||||
timeout-minutes: 10
|
||||
uses: anthropics/claude-code-action@v1
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
allowed_non_write_users: "*"
|
||||
prompt: |
|
||||
You're an oncall triage assistant for GitHub issues. Your task is to identify critical issues that require immediate oncall attention.
|
||||
|
||||
Important: Don't post any comments or messages to the issues. Your only action should be to apply the "oncall" label to qualifying issues.
|
||||
|
||||
Repository: ${{ github.repository }}
|
||||
|
||||
Task overview:
|
||||
1. Fetch all open issues updated in the last 3 days:
|
||||
- Use mcp__github__list_issues with:
|
||||
- state="open"
|
||||
- first=5 (fetch only 5 issues per page)
|
||||
- orderBy="UPDATED_AT"
|
||||
- direction="DESC"
|
||||
- This will give you the most recently updated issues first
|
||||
- For each page of results, check the updatedAt timestamp of each issue
|
||||
- Add issues updated within the last 3 days (72 hours) to your TODO list as you go
|
||||
- Keep paginating using the 'after' parameter until you encounter issues older than 3 days
|
||||
- Once you hit issues older than 3 days, you can stop fetching (no need to fetch all open issues)
|
||||
|
||||
2. Build your TODO list incrementally as you fetch:
|
||||
- As you fetch each page, immediately add qualifying issues to your TODO list
|
||||
- One TODO item per issue number (e.g., "Evaluate issue #123")
|
||||
- This allows you to start processing while still fetching more pages
|
||||
|
||||
3. For each issue in your TODO list:
|
||||
- Use mcp__github__get_issue to read the issue details (title, body, labels)
|
||||
- Use mcp__github__get_issue_comments to read all comments
|
||||
- Evaluate whether this issue needs the oncall label:
|
||||
a) Is it a bug? (has "bug" label or describes bug behavior)
|
||||
b) Does it have at least 50 engagements? (count comments + reactions)
|
||||
c) Is it truly blocking? Read and understand the full content to determine:
|
||||
- Does this prevent core functionality from working?
|
||||
- Can users work around it?
|
||||
- Consider severity indicators: "crash", "stuck", "frozen", "hang", "unresponsive", "cannot use", "blocked", "broken"
|
||||
- Be conservative - only flag issues that truly prevent users from getting work done
|
||||
|
||||
4. For issues that meet all criteria and do not already have the "oncall" label:
|
||||
- Use mcp__github__update_issue to add the "oncall" label
|
||||
- Do not post any comments
|
||||
- Do not remove any existing labels
|
||||
- Do not remove the "oncall" label from issues that already have it
|
||||
|
||||
Important guidelines:
|
||||
- Use the TODO list to track your progress through ALL candidate issues
|
||||
- Process issues efficiently - don't read every single issue upfront, work through your TODO list systematically
|
||||
- Be conservative in your assessment - only flag truly critical blocking issues
|
||||
- Do not post any comments to issues
|
||||
- Your only action should be to add the "oncall" label using mcp__github__update_issue
|
||||
- Mark each issue as complete in your TODO list as you process it
|
||||
|
||||
7. After processing all issues in your TODO list, provide a summary of your actions:
|
||||
- Total number of issues processed (candidate issues evaluated)
|
||||
- Number of issues that received the "oncall" label
|
||||
- For each issue that got the label: list issue number, title, and brief reason why it qualified
|
||||
- Close calls: List any issues that almost qualified but didn't quite meet the criteria (e.g., borderline blocking, had workarounds)
|
||||
- If no issues qualified, state that clearly
|
||||
- Format the summary clearly for easy reading
|
||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||
claude_args: |
|
||||
--mcp-config /tmp/mcp-config/mcp-servers.json
|
||||
--allowedTools "mcp__github__list_issues,mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__update_issue"
|
||||
463
CHANGELOG.md
463
CHANGELOG.md
@@ -1,445 +1,6 @@
|
||||
# Changelog
|
||||
|
||||
## 2.1.71
|
||||
|
||||
- Added `/loop` command to run a prompt or slash command on a recurring interval (e.g. `/loop 5m check the deploy`)
|
||||
- Added cron scheduling tools for recurring prompts within a session
|
||||
- Added `voice:pushToTalk` keybinding to make the voice activation key rebindable in `keybindings.json` (default: space) — modifier+letter combos like `meta+k` have zero typing interference
|
||||
- Added `fmt`, `comm`, `cmp`, `numfmt`, `expr`, `test`, `printf`, `getconf`, `seq`, `tsort`, and `pr` to the bash auto-approval allowlist
|
||||
- Fixed stdin freeze in long-running sessions where keystrokes stop being processed but the process stays alive
|
||||
- Fixed a 5–8 second startup freeze for users with voice mode enabled, caused by CoreAudio initialization blocking the main thread after system wake
|
||||
- Fixed startup UI freeze when many claude.ai proxy connectors refresh an expired OAuth token simultaneously
|
||||
- Fixed forked conversations (`/fork`) sharing the same plan file, which caused plan edits in one fork to overwrite the other
|
||||
- Fixed the Read tool putting oversized images into context when image processing failed, breaking subsequent turns in long image-heavy sessions
|
||||
- Fixed false-positive permission prompts for compound bash commands containing heredoc commit messages
|
||||
- Fixed plugin installations being lost when running multiple Claude Code instances
|
||||
- Fixed claude.ai connectors failing to reconnect after OAuth token refresh
|
||||
- Fixed claude.ai MCP connector startup notifications appearing for every org-configured connector instead of only previously connected ones
|
||||
- Fixed background agent completion notifications missing the output file path, which made it difficult for parent agents to recover agent results after context compaction
|
||||
- Fixed duplicate output in Bash tool error messages when commands exit with non-zero status
|
||||
- Fixed Chrome extension auto-detection getting permanently stuck on "not installed" after running on a machine without local Chrome
|
||||
- Fixed `/plugin marketplace update` failing with merge conflicts when the marketplace is pinned to a branch/tag ref
|
||||
- Fixed `/plugin marketplace add owner/repo@ref` incorrectly parsing `@` — previously only `#` worked as a ref separator, causing undiagnosable errors with `strictKnownMarketplaces`
|
||||
- Fixed duplicate entries in `/permissions` Workspace tab when the same directory is added with and without a trailing slash
|
||||
- Fixed `--print` hanging forever when team agents are configured — the exit loop no longer waits on long-lived `in_process_teammate` tasks
|
||||
- Fixed "❯ Tool loaded." appearing in the REPL after every `ToolSearch` call
|
||||
- Fixed prompting for `cd <cwd> && git ...` on Windows when the model uses a mingw-style path
|
||||
- Improved startup time by deferring native image processor loading to first use
|
||||
- Improved bridge session reconnection to complete within seconds after laptop wake from sleep, instead of waiting up to 10 minutes
|
||||
- Improved `/plugin uninstall` to disable project-scoped plugins in `.claude/settings.local.json` instead of modifying `.claude/settings.json`, so changes don't affect teammates
|
||||
- Improved plugin-provided MCP server deduplication — servers that duplicate a manually-configured server (same command/URL) are now skipped, preventing duplicate connections and tool sets. Suppressions are shown in the `/plugin` menu.
|
||||
- Updated `/debug` to toggle debug logging on mid-session, since debug logs are no longer written by default
|
||||
- Removed startup notification noise for unauthenticated org-registered claude.ai connectors
|
||||
|
||||
## 2.1.70
|
||||
|
||||
- Fixed API 400 errors when using `ANTHROPIC_BASE_URL` with a third-party gateway — tool search now correctly detects proxy endpoints and disables `tool_reference` blocks
|
||||
- Fixed `API Error: 400 This model does not support the effort parameter` when using custom Bedrock inference profiles or other model identifiers not matching standard Claude naming patterns
|
||||
- Fixed empty model responses immediately after `ToolSearch` — the server renders tool schemas with system-prompt-style tags at the prompt tail, which could confuse models into stopping early
|
||||
- Fixed prompt-cache bust when an MCP server with `instructions` connects after the first turn
|
||||
- Fixed Enter inserting a newline instead of submitting when typing over a slow SSH connection
|
||||
- Fixed clipboard corrupting non-ASCII text (CJK, emoji) on Windows/WSL by using PowerShell `Set-Clipboard`
|
||||
- Fixed extra VS Code windows opening at startup on Windows when running from the VS Code integrated terminal
|
||||
- Fixed voice mode failing on Windows native binary with "native audio module could not be loaded"
|
||||
- Fixed push-to-talk not activating on session start when `voiceEnabled: true` was set in settings
|
||||
- Fixed markdown links containing `#NNN` references incorrectly pointing to the current repository instead of the linked URL
|
||||
- Fixed repeated "Model updated to Opus 4.6" notification when a project's `.claude/settings.json` has a legacy Opus model string pinned
|
||||
- Fixed plugins showing as inaccurately installed in `/plugin`
|
||||
- Fixed plugins showing "not found in marketplace" errors on fresh startup by auto-refreshing after marketplace installation
|
||||
- Fixed `/security-review` command failing with `unknown option merge-base` on older git versions
|
||||
- Fixed `/color` command having no way to reset back to the default color — `/color default`, `/color gray`, `/color reset`, and `/color none` now restore the default
|
||||
- Fixed a performance regression in the `AskUserQuestion` preview dialog that re-ran markdown rendering on every keystroke in the notes input
|
||||
- Fixed feature flags read during early startup never refreshing their disk cache, causing stale values to persist across sessions
|
||||
- Fixed `permissions.defaultMode` settings values other than `acceptEdits` or `plan` being applied in Claude Code Remote environments — they are now ignored
|
||||
- Fixed skill listing being re-injected on every `--resume` (~600 tokens saved per resume)
|
||||
- Fixed teleport marker not rendering in VS Code teleported sessions
|
||||
- Improved error message when microphone captures silence to distinguish from "no speech detected"
|
||||
- Improved compaction to preserve images in the summarizer request, allowing prompt cache reuse for faster and cheaper compaction
|
||||
- Improved `/rename` to work while Claude is processing, instead of being silently queued
|
||||
- Reduced prompt input re-renders during turns by ~74%
|
||||
- Reduced startup memory by ~426KB for users without custom CA certificates
|
||||
- Reduced Remote Control `/poll` rate to once per 10 minutes while connected (was 1–2s), cutting server load ~300×. Reconnection is unaffected — transport loss immediately wakes fast polling.
|
||||
- [VSCode] Added spark icon in VS Code activity bar that lists all Claude Code sessions, with sessions opening as full editors
|
||||
- [VSCode] Added full markdown document view for plans in VS Code, with support for adding comments to provide feedback
|
||||
- [VSCode] Added native MCP server management dialog — use `/mcp` in the chat panel to enable/disable servers, reconnect, and manage OAuth authentication without switching to the terminal
|
||||
|
||||
## 2.1.69
|
||||
|
||||
- Added the `/claude-api` skill for building applications with the Claude API and Anthropic SDK
|
||||
- Added Ctrl+U on an empty bash prompt (`!`) to exit bash mode, matching `escape` and `backspace`
|
||||
- Added numeric keypad support for selecting options in Claude's interview questions (previously only the number row above QWERTY worked)
|
||||
- Added optional name argument to `/remote-control` and `claude remote-control` (`/remote-control My Project` or `--name "My Project"`) to set a custom session title visible in claude.ai/code
|
||||
- Added Voice STT support for 10 new languages (20 total) — Russian, Polish, Turkish, Dutch, Ukrainian, Greek, Czech, Danish, Swedish, Norwegian
|
||||
- Added effort level display (e.g., "with low effort") to the logo and spinner, making it easier to see which effort setting is active
|
||||
- Added agent name display in terminal title when using `claude --agent`
|
||||
- Added `sandbox.enableWeakerNetworkIsolation` setting (macOS only) to allow Go programs like `gh`, `gcloud`, and `terraform` to verify TLS certificates when using a custom MITM proxy with `httpProxyPort`
|
||||
- Added `includeGitInstructions` setting (and `CLAUDE_CODE_DISABLE_GIT_INSTRUCTIONS` env var) to remove built-in commit and PR workflow instructions from Claude's system prompt
|
||||
- Added `/reload-plugins` command to activate pending plugin changes without restarting
|
||||
- Added a one-time startup prompt suggesting Claude Code Desktop on macOS and Windows (max 3 showings, dismissible)
|
||||
- Added `${CLAUDE_SKILL_DIR}` variable for skills to reference their own directory in SKILL.md content
|
||||
- Added `InstructionsLoaded` hook event that fires when CLAUDE.md or `.claude/rules/*.md` files are loaded into context
|
||||
- Added `agent_id` (for subagents) and `agent_type` (for subagents and `--agent`) to hook events
|
||||
- Added `worktree` field to status line hook commands with name, path, branch, and original repo directory when running in a `--worktree` session
|
||||
- Added `pluginTrustMessage` in managed settings to append organization-specific context to the plugin trust warning shown before installation
|
||||
- Added policy limit fetching (e.g., remote control restrictions) for Team plan OAuth users, not just Enterprise
|
||||
- Added `pathPattern` to `strictKnownMarketplaces` for regex-matching file/directory marketplace sources alongside `hostPattern` restrictions
|
||||
- Added plugin source type `git-subdir` to point to a subdirectory within a git repo
|
||||
- Added `oauth.authServerMetadataUrl` config option for MCP servers to specify a custom OAuth metadata discovery URL when standard discovery fails
|
||||
- Fixed a security issue where nested skill discovery could load skills from gitignored directories like `node_modules`
|
||||
- Fixed trust dialog silently enabling all `.mcp.json` servers on first run. You'll now see the per-server approval dialog as expected
|
||||
- Fixed `claude remote-control` crashing immediately on npm installs with "bad option: --sdk-url" (anthropics/claude-code#28334)
|
||||
- Fixed `--model claude-opus-4-0` and `--model claude-opus-4-1` resolving to deprecated Opus versions instead of current
|
||||
- Fixed macOS keychain corruption when using multiple OAuth MCP servers. Large OAuth metadata blobs could overflow the `security -i` stdin buffer, silently leaving stale credentials behind and causing repeated `/login` prompts.
|
||||
- Fixed `.credentials.json` losing `subscriptionType` (showing "Claude API" instead of "Claude Pro"/"Claude Max") when the profile endpoint transiently fails during token refresh (anthropics/claude-code#30185)
|
||||
- Fixed ghost dotfiles (`.bashrc`, `HEAD`, etc.) appearing as untracked files in the working directory after sandboxed Bash commands on Linux
|
||||
- Fixed Shift+Enter printing `[27;2;13~` instead of inserting a newline in Ghostty over SSH
|
||||
- Fixed stash (Ctrl+S) being cleared when submitting a message while Claude is working
|
||||
- Fixed ctrl+o (transcript toggle) freezing for many seconds in long sessions with lots of file edits
|
||||
- Fixed plan mode feedback input not supporting multi-line text entry (backslash+Enter and Shift+Enter now insert newlines)
|
||||
- Fixed cursor not moving down into blank lines at the top of the input box
|
||||
- Fixed `/stats` crash when transcript files contain entries with missing or malformed timestamps
|
||||
- Fixed a brief hang after a streaming error on long sessions (the transcript was being fully rewritten to drop one line; it is now truncated in place)
|
||||
- Fixed `--setting-sources user` not blocking dynamically discovered project skills
|
||||
- Fixed duplicate CLAUDE.md, slash commands, agents, and rules when running from a worktree nested inside its main repo (e.g. `claude -w`)
|
||||
- Fixed plugin Stop/SessionEnd/etc hooks not firing after any `/plugin` operation
|
||||
- Fixed plugin hooks being silently dropped when two plugins use the same `${CLAUDE_PLUGIN_ROOT}/...` command template
|
||||
- Fixed memory leak in long-running SDK/CCR sessions where conversation messages were retained unnecessarily
|
||||
- Fixed API 400 errors in forked agents (autocompact, summarization) when resuming sessions that were interrupted mid-tool-batch
|
||||
- Fixed "unexpected tool_use_id found in tool_result blocks" error when resuming conversations that start with an orphaned tool result
|
||||
- Fixed teammates accidentally spawning nested teammates via the Agent tool's `name` parameter
|
||||
- Fixed `CLAUDE_CODE_MAX_OUTPUT_TOKENS` being ignored during conversation compaction
|
||||
- Fixed `/compact` summary rendering as a user bubble in SDK consumers (Claude Code Remote web UI, VSCode extension)
|
||||
- Fixed voice space bar getting stuck after a failed voice activation (module loading race, cold GrowthBook)
|
||||
- Fixed worktree file copy on Windows
|
||||
- Fixed global `.claude` folder detection on Windows
|
||||
- Fixed symlink bypass where writing new files through a symlinked parent directory could escape the working directory in `acceptEdits` mode
|
||||
- Fixed sandbox prompting users to approve non-allowed domains when `allowManagedDomainsOnly` is enabled in managed settings — non-allowed domains are now blocked automatically with no bypass
|
||||
- Fixed interactive tools (e.g., `AskUserQuestion`) being silently auto-allowed when listed in a skill's allowed-tools, bypassing the permission prompt and running with empty answers
|
||||
- Fixed multi-GB memory spike when committing with large untracked binary files in the working tree
|
||||
- Fixed Escape not interrupting a running turn when the input box has draft text. Use Up arrow to pull queued messages back for editing, or Ctrl+U to clear the input line.
|
||||
- Fixed Android app crash when running local slash commands (`/voice`, `/cost`) in Remote Control sessions
|
||||
- Fixed a memory leak where old message array versions accumulated in React Compiler `memoCache` over long sessions
|
||||
- Fixed a memory leak where REPL render scopes accumulated over long sessions (~35MB over 1000 turns)
|
||||
- Fixed memory retention in in-process teammates where the parent's full conversation history was pinned for the teammate's lifetime, preventing GC after `/clear` or auto-compact
|
||||
- Fixed a memory leak in interactive mode where hook events could accumulate unboundedly during long sessions
|
||||
- Fixed hang when `--mcp-config` points to a corrupted file
|
||||
- Fixed slow startup when many skills/plugins are installed
|
||||
- Fixed `cd <outside-dir> && <cmd>` permission prompt to surface the chained command instead of only showing "Yes, allow reading from <dir>/"
|
||||
- Fixed conditional `.claude/rules/*.md` files (with `paths:` frontmatter) and nested CLAUDE.md files not loading in print mode (`claude -p`)
|
||||
- Fixed `/clear` not fully clearing all session caches, reducing memory retention in long sessions
|
||||
- Fixed terminal flicker caused by animated elements at the scrollback boundary
|
||||
- Fixed UI frame drops on macOS when using MCP servers with OAuth (regression from 2.1.x)
|
||||
- Fixed occasional frame stalls during typing caused by synchronous debug log flushes
|
||||
- Fixed `TeammateIdle` and `TaskCompleted` hooks to support `{"continue": false, "stopReason": "..."}` to stop the teammate, matching `Stop` hook behavior
|
||||
- Fixed `WorktreeCreate` and `WorktreeRemove` plugin hooks being silently ignored
|
||||
- Fixed skill descriptions with colons (e.g., "Triggers include: X, Y, Z") failing to load from SKILL.md frontmatter
|
||||
- Fixed project skills without a `description:` frontmatter field not appearing in Claude's available skills list
|
||||
- Fixed `/context` showing identical token counts for all MCP tools from a server
|
||||
- Fixed literal `nul` file creation on Windows when the model uses CMD-style `2>nul` redirection in Git Bash
|
||||
- Fixed extra blank lines appearing below each tool call in the expanded subagent transcript view (Ctrl+O)
|
||||
- Fixed Tab/arrow keys not cycling Settings tabs when `/config` search box is focused but empty
|
||||
- Fixed service key OAuth sessions (CCR containers) spamming `[ERROR]` logs with 403s from profile-scoped endpoints
|
||||
- Fixed inconsistent color for "Remote Control active" status indicator
|
||||
- Fixed Voice waveform cursor covering the first suffix letter when dictating mid-input
|
||||
- Fixed Voice input showing all 5 spaces during warmup instead of capping at ~2 (aligning with the "keep holding…" hint)
|
||||
- Improved spinner performance by isolating the 50ms animation loop from the surrounding shell, reducing render and CPU overhead during turns
|
||||
- Improved UI rendering performance in native binaries with React Compiler
|
||||
- Improved `--worktree` startup by eliminating a git subprocess on the startup path
|
||||
- Improved macOS startup by eliminating redundant settings-file reloads when managed settings resolve
|
||||
- Improved macOS startup for Claude.ai enterprise/team users by skipping an unnecessary keychain lookup
|
||||
- Improved MCP `-p` startup by pipelining claude.ai config fetch with local connections and using a concurrency pool instead of sequential batching
|
||||
- Improved voice startup by removing imperceptible warmup pulse animations that were causing re-render stutter
|
||||
- Improved MCP binary content handling: tools returning PDFs, Office documents, or audio now save decoded bytes to disk with the correct file extension instead of dumping raw base64 into the conversation context. WebFetch also saves binary responses alongside its summary.
|
||||
- Improved memory usage in long sessions by stabilizing `onSubmit` across message updates
|
||||
- Improved LSP tool rendering and memory context building to no longer read entire files
|
||||
- Improved session upload and memory sync to avoid reading large files into memory before size/binary checks
|
||||
- Improved file operation performance by avoiding reading file contents for existence checks (6 sites)
|
||||
- Improved documentation to clarify that `--append-system-prompt-file` and `--system-prompt-file` work in interactive mode (the docs previously said print mode only)
|
||||
- Reduced baseline memory by ~16MB by deferring Yoga WASM preloading
|
||||
- Reduced memory footprint for SDK and CCR sessions using stream-json output
|
||||
- Reduced memory usage when resuming large sessions (including compacted history)
|
||||
- Reduced token usage on multi-agent tasks with more concise subagent final reports
|
||||
- Changed Sonnet 4.5 users on Pro/Max/Team Premium to be automatically migrated to Sonnet 4.6
|
||||
- Changed the `/resume` picker to show your most recent prompt instead of the first one. This also resolves some titles appearing as `(session)`.
|
||||
- Changed claude.ai MCP connector failures to show a notification instead of silently disappearing from the tool list
|
||||
- Changed example command suggestions to be generated deterministically instead of calling Haiku
|
||||
- Changed resuming after compaction to no longer produce a preamble recap before continuing
|
||||
- [SDK] Changed task creation to no longer require the `activeForm` field — the spinner falls back to the task subject
|
||||
- [VSCode] Added compaction display as a collapsible "Compacted chat" card with the summary inside
|
||||
- [VSCode] The permission mode picker now respects `permissions.disableBypassPermissionsMode` from your effective Claude Code settings (including managed/policy settings) — when set to `disable`, bypass permissions mode is hidden from the picker
|
||||
- [VSCode] Fixed RTL text (Arabic, Hebrew, Persian) rendering reversed in the chat panel (regression in v2.1.63)
|
||||
|
||||
## 2.1.68
|
||||
|
||||
- Opus 4.6 now defaults to medium effort for Max and Team subscribers. Medium effort works well for most tasks — it's the sweet spot between speed and thoroughness. You can change this anytime with `/model`
|
||||
- Re-introduced the "ultrathink" keyword to enable high effort for the next turn
|
||||
- Removed Opus 4 and 4.1 from Claude Code on the first-party API — users with these models pinned are automatically moved to Opus 4.6
|
||||
|
||||
## 2.1.66
|
||||
|
||||
- Reduced spurious error logging
|
||||
|
||||
## 2.1.63
|
||||
|
||||
- Added `/simplify` and `/batch` bundled slash commands
|
||||
- Fixed local slash command output like /cost appearing as user-sent messages instead of system messages in the UI
|
||||
- Project configs & auto memory now shared across git worktrees of the same repository
|
||||
- Added `ENABLE_CLAUDEAI_MCP_SERVERS=false` env var to opt out from making claude.ai MCP servers available
|
||||
- Improved `/model` command to show the currently active model in the slash command menu
|
||||
- Added HTTP hooks, which can POST JSON to a URL and receive JSON instead of running a shell command
|
||||
- Fixed listener leak in bridge polling loop
|
||||
- Fixed listener leak in MCP OAuth flow cleanup
|
||||
- Added manual URL paste fallback during MCP OAuth authentication. If the automatic localhost redirect doesn't work, you can paste the callback URL to complete authentication.
|
||||
- Fixed memory leak when navigating hooks configuration menu
|
||||
- Fixed listener leak in interactive permission handler during auto-approvals
|
||||
- Fixed file count cache ignoring glob ignore patterns
|
||||
- Fixed memory leak in bash command prefix cache
|
||||
- Fixed MCP tool/resource cache leak on server reconnect
|
||||
- Fixed IDE host IP detection cache incorrectly sharing results across ports
|
||||
- Fixed WebSocket listener leak on transport reconnect
|
||||
- Fixed memory leak in git root detection cache that could cause unbounded growth in long-running sessions
|
||||
- Fixed memory leak in JSON parsing cache that grew unbounded over long sessions
|
||||
- VSCode: Fixed remote sessions not appearing in conversation history
|
||||
- Fixed a race condition in the REPL bridge where new messages could arrive at the server interleaved with historical messages during the initial connection flush, causing message ordering issues.
|
||||
- Fixed memory leak where long-running teammates retained all messages in AppState even after conversation compaction
|
||||
- Fixed a memory leak where MCP server fetch caches were not cleared on disconnect, causing growing memory usage with servers that reconnect frequently
|
||||
- Improved memory usage in long sessions with subagents by stripping heavy progress message payloads during context compaction
|
||||
- Added "Always copy full response" option to the `/copy` picker. When selected, future `/copy` commands will skip the code block picker and copy the full response directly.
|
||||
- VSCode: Added session rename and remove actions to the sessions list
|
||||
- Fixed `/clear` not resetting cached skills, which could cause stale skill content to persist in the new conversation
|
||||
|
||||
## 2.1.62
|
||||
|
||||
- Fixed prompt suggestion cache regression that reduced cache hit rates
|
||||
|
||||
## 2.1.61
|
||||
|
||||
- Fixed concurrent writes corrupting config file on Windows
|
||||
|
||||
## 2.1.59
|
||||
|
||||
- Claude automatically saves useful context to auto-memory. Manage with /memory
|
||||
- Added `/copy` command to show an interactive picker when code blocks are present, allowing selection of individual code blocks or the full response.
|
||||
- Improved "always allow" prefix suggestions for compound bash commands (e.g. `cd /tmp && git fetch && git push`) to compute smarter per-subcommand prefixes instead of treating the whole command as one
|
||||
- Improved ordering of short task lists
|
||||
- Improved memory usage in multi-agent sessions by releasing completed subagent task state
|
||||
- Fixed MCP OAuth token refresh race condition when running multiple Claude Code instances simultaneously
|
||||
- Fixed shell commands not showing a clear error message when the working directory has been deleted
|
||||
- Fixed config file corruption that could wipe authentication when multiple Claude Code instances ran simultaneously
|
||||
|
||||
## 2.1.58
|
||||
|
||||
- Expand Remote Control to more users
|
||||
|
||||
## 2.1.56
|
||||
|
||||
- VS Code: Fixed another cause of "command 'claude-vscode.editor.openLast' not found" crashes
|
||||
|
||||
## 2.1.55
|
||||
|
||||
- Fixed BashTool failing on Windows with EINVAL error
|
||||
|
||||
## 2.1.53
|
||||
|
||||
- Fixed a UI flicker where user input would briefly disappear after submission before the message rendered
|
||||
- Fixed bulk agent kill (ctrl+f) to send a single aggregate notification instead of one per agent, and to properly clear the command queue
|
||||
- Fixed graceful shutdown sometimes leaving stale sessions when using Remote Control by parallelizing teardown network calls
|
||||
- Fixed `--worktree` sometimes being ignored on first launch
|
||||
- Fixed a panic ("switch on corrupted value") on Windows
|
||||
- Fixed a crash that could occur when spawning many processes on Windows
|
||||
- Fixed a crash in the WebAssembly interpreter on Linux x64 & Windows x64
|
||||
- Fixed a crash that sometimes occurred after 2 minutes on Windows ARM64
|
||||
|
||||
## 2.1.52
|
||||
|
||||
- VS Code: Fixed extension crash on Windows ("command 'claude-vscode.editor.openLast' not found")
|
||||
|
||||
## 2.1.51
|
||||
|
||||
- Added `claude remote-control` subcommand for external builds, enabling local environment serving for all users.
|
||||
- Updated plugin marketplace default git timeout from 30s to 120s and added `CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS` to configure.
|
||||
- Added support for custom npm registries and specific version pinning when installing plugins from npm sources
|
||||
- BashTool now skips login shell (`-l` flag) by default when a shell snapshot is available, improving command execution performance. Previously this required setting `CLAUDE_BASH_NO_LOGIN=true`.
|
||||
- Fixed a security issue where `statusLine` and `fileSuggestion` hook commands could execute without workspace trust acceptance in interactive mode.
|
||||
- Tool results larger than 50K characters are now persisted to disk (previously 100K). This reduces context window usage and improves conversation longevity.
|
||||
- Fixed a bug where duplicate `control_response` messages (e.g. from WebSocket reconnects) could cause API 400 errors by pushing duplicate assistant messages into the conversation.
|
||||
- Added `CLAUDE_CODE_ACCOUNT_UUID`, `CLAUDE_CODE_USER_EMAIL`, and `CLAUDE_CODE_ORGANIZATION_UUID` environment variables for SDK callers to provide account info synchronously, eliminating a race condition where early telemetry events lacked account metadata.
|
||||
- Fixed slash command autocomplete crashing when a plugin's SKILL.md description is a YAML array or other non-string type
|
||||
- The `/model` picker now shows human-readable labels (e.g., "Sonnet 4.5") instead of raw model IDs for pinned model versions, with an upgrade hint when a newer version is available.
|
||||
- Managed settings can now be set via macOS plist or Windows Registry. Learn more at https://code.claude.com/docs/en/settings#settings-files
|
||||
|
||||
## 2.1.50
|
||||
|
||||
- Added support for `startupTimeout` configuration for LSP servers
|
||||
- Added `WorktreeCreate` and `WorktreeRemove` hook events, enabling custom VCS setup and teardown when agent worktree isolation creates or removes worktrees.
|
||||
- Fixed a bug where resumed sessions could be invisible when the working directory involved symlinks, because the session storage path was resolved at different times during startup. Also fixed session data loss on SSH disconnect by flushing session data before hooks and analytics in the graceful shutdown sequence.
|
||||
- Linux: Fixed native modules not loading on systems with glibc older than 2.30 (e.g., RHEL 8)
|
||||
- Fixed memory leak in agent teams where completed teammate tasks were never garbage collected from session state
|
||||
- Fixed `CLAUDE_CODE_SIMPLE` to fully strip down skills, session memory, custom agents, and CLAUDE.md token counting
|
||||
- Fixed `/mcp reconnect` freezing the CLI when given a server name that doesn't exist
|
||||
- Fixed memory leak where completed task state objects were never removed from AppState
|
||||
- Added support for `isolation: worktree` in agent definitions, allowing agents to declaratively run in isolated git worktrees.
|
||||
- `CLAUDE_CODE_SIMPLE` mode now also disables MCP tools, attachments, hooks, and CLAUDE.md file loading for a fully minimal experience.
|
||||
- Fixed bug where MCP tools were not discovered when tool search is enabled and a prompt is passed in as a launch argument
|
||||
- Improved memory usage during long sessions by clearing internal caches after compaction
|
||||
- Added `claude agents` CLI command to list all configured agents
|
||||
- Improved memory usage during long sessions by clearing large tool results after they have been processed
|
||||
- Fixed a memory leak where LSP diagnostic data was never cleaned up after delivery, causing unbounded memory growth in long sessions
|
||||
- Fixed a memory leak where completed task output was not freed from memory, reducing memory usage in long sessions with many tasks
|
||||
- Improved startup performance for headless mode (`-p` flag) by deferring Yoga WASM and UI component imports
|
||||
- Fixed prompt suggestion cache regression that reduced cache hit rates
|
||||
- Fixed unbounded memory growth in long sessions by capping file history snapshots
|
||||
- Added `CLAUDE_CODE_DISABLE_1M_CONTEXT` environment variable to disable 1M context window support
|
||||
- Opus 4.6 (fast mode) now includes the full 1M context window
|
||||
- VSCode: Added `/extra-usage` command support in VS Code sessions
|
||||
- Fixed memory leak where TaskOutput retained recent lines after cleanup
|
||||
- Fixed memory leak in CircularBuffer where cleared items were retained in the backing array
|
||||
- Fixed memory leak in shell command execution where ChildProcess and AbortController references were retained after cleanup
|
||||
|
||||
## 2.1.49
|
||||
|
||||
- Improved MCP OAuth authentication with step-up auth support and discovery caching, reducing redundant network requests during server connections
|
||||
- Added `--worktree` (`-w`) flag to start Claude in an isolated git worktree
|
||||
- Subagents support `isolation: "worktree"` for working in a temporary git worktree
|
||||
- Added Ctrl+F keybinding to kill background agents (two-press confirmation)
|
||||
- Agent definitions support `background: true` to always run as a background task
|
||||
- Plugins can ship `settings.json` for default configuration
|
||||
- Fixed file-not-found errors to suggest corrected paths when the model drops the repo folder
|
||||
- Fixed Ctrl+C and ESC being silently ignored when background agents are running and the main thread is idle. Pressing twice within 3 seconds now kills all background agents.
|
||||
- Fixed prompt suggestion cache regression that reduced cache hit rates.
|
||||
- Fixed `plugin enable` and `plugin disable` to auto-detect the correct scope when `--scope` is not specified, instead of always defaulting to user scope
|
||||
- Simple mode (`CLAUDE_CODE_SIMPLE`) now includes the file edit tool in addition to the Bash tool, allowing direct file editing in simple mode.
|
||||
- Permission suggestions are now populated when safety checks trigger an ask response, enabling SDK consumers to display permission options
|
||||
- Sonnet 4.5 with 1M context is being removed from the Max plan in favor of our frontier Sonnet 4.6 model, which now has 1M context. Please switch in /model.
|
||||
- Fixed verbose mode not updating thinking block display when toggled via `/config` — memo comparators now correctly detect verbose changes
|
||||
- Fixed unbounded WASM memory growth during long sessions by periodically resetting the tree-sitter parser
|
||||
- Fixed potential rendering issues caused by stale yoga layout references
|
||||
- Improved performance in non-interactive mode (`-p`) by skipping unnecessary API calls during startup
|
||||
- Improved performance by caching authentication failures for HTTP and SSE MCP servers, avoiding repeated connection attempts to servers requiring auth
|
||||
- Fixed unbounded memory growth during long-running sessions caused by Yoga WASM linear memory never shrinking
|
||||
- SDK model info now includes `supportsEffort`, `supportedEffortLevels`, and `supportsAdaptiveThinking` fields so consumers can discover model capabilities.
|
||||
- Added `ConfigChange` hook event that fires when configuration files change during a session, enabling enterprise security auditing and optional blocking of settings changes.
|
||||
- Improved startup performance by caching MCP auth failures to avoid redundant connection attempts
|
||||
- Improved startup performance by reducing HTTP calls for analytics token counting
|
||||
- Improved startup performance by batching MCP tool token counting into a single API call
|
||||
- Fixed `disableAllHooks` setting to respect managed settings hierarchy — non-managed settings can no longer disable managed hooks set by policy (#26637)
|
||||
- Fixed `--resume` session picker showing raw XML tags for sessions that start with commands like `/clear`. Now correctly falls through to the session ID fallback.
|
||||
- Improved permission prompts for path safety and working directory blocks to show the reason for the restriction instead of a bare prompt with no context
|
||||
|
||||
## 2.1.47
|
||||
|
||||
- Fixed FileWriteTool line counting to preserve intentional trailing blank lines instead of stripping them with `trimEnd()`.
|
||||
- Fixed Windows terminal rendering bugs caused by `os.EOL` (`\r\n`) in display code — line counts now show correct values instead of always showing 1 on Windows.
|
||||
- Improved VS Code plan preview: auto-updates as Claude iterates, enables commenting only when the plan is ready for review, and keeps the preview open when rejecting so Claude can revise.
|
||||
- Fixed a bug where bold and colored text in markdown output could shift to the wrong characters on Windows due to `\r\n` line endings.
|
||||
- Fixed compaction failing when conversation contains many PDF documents by stripping document blocks alongside images before sending to the compaction API (anthropics/claude-code#26188)
|
||||
- Improved memory usage in long-running sessions by releasing API stream buffers, agent context, and skill state after use
|
||||
- Improved startup performance by deferring SessionStart hook execution, reducing time-to-interactive by ~500ms.
|
||||
- Fixed an issue where bash tool output was silently discarded on Windows when using MSYS2 or Cygwin shells.
|
||||
- Improved performance of `@` file mentions - file suggestions now appear faster by pre-warming the index on startup and using session-based caching with background refresh.
|
||||
- Improved memory usage by trimming agent task message history after tasks complete
|
||||
- Improved memory usage during long agent sessions by eliminating O(n²) message accumulation in progress updates
|
||||
- Fixed the bash permission classifier to validate that returned match descriptions correspond to actual input rules, preventing hallucinated descriptions from incorrectly granting permissions
|
||||
- Fixed user-defined agents only loading one file on NFS/FUSE filesystems that report zero inodes (anthropics/claude-code#26044)
|
||||
- Fixed plugin agent skills silently failing to load when referenced by bare name instead of fully-qualified plugin name (anthropics/claude-code#25834)
|
||||
- Search patterns in collapsed tool results are now displayed in quotes for clarity
|
||||
- Windows: Fixed CWD tracking temp files never being cleaned up, causing them to accumulate indefinitely (anthropics/claude-code#17600)
|
||||
- Use `ctrl+f` to kill all background agents instead of double-pressing ESC. Background agents now continue running when you press ESC to cancel the main thread, giving you more control over agent lifecycle.
|
||||
- Fixed API 400 errors ("thinking blocks cannot be modified") that occurred in sessions with concurrent agents, caused by interleaved streaming content blocks preventing proper message merging.
|
||||
- Simplified teammate navigation to use only Shift+Down (with wrapping) instead of both Shift+Up and Shift+Down.
|
||||
- Fixed an issue where a single file write/edit error would abort all other parallel file write/edit operations. Independent file mutations now complete even when a sibling fails.
|
||||
- Added `last_assistant_message` field to Stop and SubagentStop hook inputs, providing the final assistant response text so hooks can access it without parsing transcript files.
|
||||
- Fixed custom session titles set via `/rename` being lost after resuming a conversation (anthropics/claude-code#23610)
|
||||
- Fixed collapsed read/search hint text overflowing on narrow terminals by truncating from the start.
|
||||
- Fixed an issue where bash commands with backslash-newline continuation lines (e.g., long commands split across multiple lines with `\`) would produce spurious empty arguments, potentially breaking command execution.
|
||||
- Fixed built-in slash commands (`/help`, `/model`, `/compact`, etc.) being hidden from the autocomplete dropdown when many user skills are installed (anthropics/claude-code#22020)
|
||||
- Fixed MCP servers not appearing in the MCP Management Dialog after deferred loading
|
||||
- Fixed session name persisting in status bar after `/clear` command (anthropics/claude-code#26082)
|
||||
- Fixed crash when a skill's `name` or `description` in SKILL.md frontmatter is a bare number (e.g., `name: 3000`) — the value is now properly coerced to a string (anthropics/claude-code#25837)
|
||||
- Fixed /resume silently dropping sessions when the first message exceeds 16KB or uses array-format content (anthropics/claude-code#25721)
|
||||
- Added `chat:newline` keybinding action for configurable multi-line input (anthropics/claude-code#26075)
|
||||
- Added `added_dirs` to the statusline JSON `workspace` section, exposing directories added via `/add-dir` to external scripts (anthropics/claude-code#26096)
|
||||
- Fixed `claude doctor` misclassifying mise and asdf-managed installations as native installs (anthropics/claude-code#26033)
|
||||
- Fixed zsh heredoc failing with "read-only file system" error in sandboxed commands (anthropics/claude-code#25990)
|
||||
- Fixed agent progress indicator showing inflated tool use count (anthropics/claude-code#26023)
|
||||
- Fixed image pasting not working on WSL2 systems where Windows copies images as BMP format (anthropics/claude-code#25935)
|
||||
- Fixed background agent results returning raw transcript data instead of the agent's final answer (anthropics/claude-code#26012)
|
||||
- Fixed Warp terminal incorrectly prompting for Shift+Enter setup when it supports it natively (anthropics/claude-code#25957)
|
||||
- Fixed CJK wide characters causing misaligned timestamps and layout elements in the TUI (anthropics/claude-code#26084)
|
||||
- Fixed custom agent `model` field in `.claude/agents/*.md` being ignored when spawning team teammates (anthropics/claude-code#26064)
|
||||
- Fixed plan mode being lost after context compaction, causing the model to switch from planning to implementation mode (anthropics/claude-code#26061)
|
||||
- Fixed `alwaysThinkingEnabled: true` in settings.json not enabling thinking mode on Bedrock and Vertex providers (anthropics/claude-code#26074)
|
||||
- Fixed `tool_decision` OTel telemetry event not being emitted in headless/SDK mode (anthropics/claude-code#26059)
|
||||
- Fixed session name being lost after context compaction — renamed sessions now preserve their custom title through compaction (anthropics/claude-code#26121)
|
||||
- Increased initial session count in resume picker from 10 to 50 for faster session discovery (anthropics/claude-code#26123)
|
||||
- Windows: fixed worktree session matching when drive letter casing differs (anthropics/claude-code#26123)
|
||||
- Fixed `/resume <session-id>` failing to find sessions whose first message exceeds 16KB (anthropics/claude-code#25920)
|
||||
- Fixed "Always allow" on multiline bash commands creating invalid permission patterns that corrupt settings (anthropics/claude-code#25909)
|
||||
- Fixed React crash (error #31) when a skill's `argument-hint` in SKILL.md frontmatter uses YAML sequence syntax (e.g., `[topic: foo | bar]`) — the value is now properly coerced to a string (anthropics/claude-code#25826)
|
||||
- Fixed crash when using `/fork` on sessions that used web search — null entries in search results from transcript deserialization are now handled gracefully (anthropics/claude-code#25811)
|
||||
- Fixed read-only git commands triggering FSEvents file watcher loops on macOS by adding --no-optional-locks flag (anthropics/claude-code#25750)
|
||||
- Fixed custom agents and skills not being discovered when running from a git worktree — project-level `.claude/agents/` and `.claude/skills/` from the main repository are now included (anthropics/claude-code#25816)
|
||||
- Fixed non-interactive subcommands like `claude doctor` and `claude plugin validate` being blocked inside nested Claude sessions (anthropics/claude-code#25803)
|
||||
- Windows: Fixed the same CLAUDE.md file being loaded twice when drive letter casing differs between paths (anthropics/claude-code#25756)
|
||||
- Fixed inline code spans in markdown being incorrectly parsed as bash commands (anthropics/claude-code#25792)
|
||||
- Fixed teammate spinners not respecting custom spinnerVerbs from settings (anthropics/claude-code#25748)
|
||||
- Fixed shell commands permanently failing after a command deletes its own working directory (anthropics/claude-code#26136)
|
||||
- Fixed hooks (PreToolUse, PostToolUse) silently failing to execute on Windows by using Git Bash instead of cmd.exe (anthropics/claude-code#25981)
|
||||
- Fixed LSP `findReferences` and other location-based operations returning results from gitignored files (e.g., `node_modules/`, `venv/`) (anthropics/claude-code#26051)
|
||||
- Moved config backup files from home directory root to `~/.claude/backups/` to reduce home directory clutter (anthropics/claude-code#26130)
|
||||
- Fixed sessions with large first prompts (>16KB) disappearing from the /resume list (anthropics/claude-code#26140)
|
||||
- Fixed shell functions with double-underscore prefixes (e.g., `__git_ps1`) not being preserved across shell sessions (anthropics/claude-code#25824)
|
||||
- Fixed spinner showing "0 tokens" counter before any tokens have been received (anthropics/claude-code#26105)
|
||||
- VSCode: Fixed conversation messages appearing dimmed while the AskUserQuestion dialog is open (anthropics/claude-code#26078)
|
||||
- Fixed background tasks failing in git worktrees due to remote URL resolution reading from worktree-specific gitdir instead of the main repository config (anthropics/claude-code#26065)
|
||||
- Fixed Right Alt key leaving visible `[25~` escape sequence residue in the input field on Windows/Git Bash terminals (anthropics/claude-code#25943)
|
||||
- The `/rename` command now updates the terminal tab title by default (anthropics/claude-code#25789)
|
||||
- Fixed Edit tool silently corrupting Unicode curly quotes (\u201c\u201d \u2018\u2019) by replacing them with straight quotes when making edits (anthropics/claude-code#26141)
|
||||
- Fixed OSC 8 hyperlinks only being clickable on the first line when link text wraps across multiple terminal lines.
|
||||
|
||||
## 2.1.46
|
||||
|
||||
- Fixed orphaned CC processes after terminal disconnect on macOS
|
||||
- Added support for using claude.ai MCP connectors in Claude Code
|
||||
|
||||
## 2.1.45
|
||||
|
||||
- Added support for Claude Sonnet 4.6
|
||||
- Added support for reading `enabledPlugins` and `extraKnownMarketplaces` from `--add-dir` directories
|
||||
- Added `spinnerTipsOverride` setting to customize spinner tips — configure `tips` with an array of custom tip strings, and optionally set `excludeDefault: true` to show only your custom tips instead of the built-in ones
|
||||
- Added `SDKRateLimitInfo` and `SDKRateLimitEvent` types to the SDK, enabling consumers to receive rate limit status updates including utilization, reset times, and overage information
|
||||
- Fixed Agent Teams teammates failing on Bedrock, Vertex, and Foundry by propagating API provider environment variables to tmux-spawned processes (anthropics/claude-code#23561)
|
||||
- Fixed sandbox "operation not permitted" errors when writing temporary files on macOS by using the correct per-user temp directory (anthropics/claude-code#21654)
|
||||
- Fixed Task tool (backgrounded agents) crashing with a `ReferenceError` on completion (anthropics/claude-code#22087)
|
||||
- Fixed autocomplete suggestions not being accepted on Enter when images are pasted in the input
|
||||
- Fixed skills invoked by subagents incorrectly appearing in main session context after compaction
|
||||
- Fixed excessive `.claude.json.backup` files accumulating on every startup
|
||||
- Fixed plugin-provided commands, agents, and hooks not being available immediately after installation without requiring a restart
|
||||
- Improved startup performance by removing eager loading of session history for stats caching
|
||||
- Improved memory usage for shell commands that produce large output — RSS no longer grows unboundedly with command output size
|
||||
- Improved collapsed read/search groups to show the current file or search pattern being processed beneath the summary line while active
|
||||
- [VSCode] Improved permission destination choice (project/user/session) to persist across sessions
|
||||
|
||||
## 2.1.44
|
||||
|
||||
- Fixed ENAMETOOLONG errors for deeply-nested directory paths
|
||||
- Fixed auth refresh errors
|
||||
|
||||
## 2.1.43
|
||||
|
||||
- Fixed AWS auth refresh hanging indefinitely by adding a 3-minute timeout
|
||||
- Fixed spurious warnings for non-agent markdown files in `.claude/agents/` directory
|
||||
- Fixed structured-outputs beta header being sent unconditionally on Vertex/Bedrock
|
||||
|
||||
## 2.1.42
|
||||
|
||||
- Improved startup performance by deferring Zod schema construction
|
||||
- Improved prompt cache hit rates by moving date out of system prompt
|
||||
- Added one-time Opus 4.6 effort callout for eligible users
|
||||
- Fixed /resume showing interrupt messages as session titles
|
||||
- Fixed image dimension limit errors to suggest /compact
|
||||
|
||||
## 2.1.41
|
||||
## 2.1.39
|
||||
|
||||
- Added guard against launching Claude Code inside another Claude Code session
|
||||
- Fixed Agent Teams using wrong model identifier for Bedrock, Vertex, and Foundry customers
|
||||
@@ -449,23 +10,11 @@
|
||||
- Fixed plugin browse showing misleading "Space to Toggle" hint for already-installed plugins
|
||||
- Fixed hook blocking errors (exit code 2) not showing stderr to the user
|
||||
- Added `speed` attribute to OTel events and trace spans for fast mode visibility
|
||||
- Added `claude auth login`, `claude auth status`, and `claude auth logout` CLI subcommands
|
||||
- Added Windows ARM64 (win32-arm64) native binary support
|
||||
- Improved `/rename` to auto-generate session name from conversation context when called without arguments
|
||||
- Improved narrow terminal layout for prompt footer
|
||||
- Fixed file resolution failing for @-mentions with anchor fragments (e.g., `@README.md#installation`)
|
||||
- Fixed FileReadTool blocking the process on FIFOs, `/dev/stdin`, and large files
|
||||
- Fixed background task notifications not being delivered in streaming Agent SDK mode
|
||||
- Fixed cursor jumping to end on each keystroke in classifier rule input
|
||||
- Fixed markdown link display text being dropped for raw URL
|
||||
- Fixed auto-compact failure error notifications being shown to users
|
||||
- Fixed permission wait time being included in subagent elapsed time display
|
||||
- Fixed proactive ticks firing while in plan mode
|
||||
- Fixed clear stale permission rules when settings change on disk
|
||||
- Fixed hook blocking errors showing stderr content in UI
|
||||
|
||||
## 2.1.39
|
||||
|
||||
- Fixed /resume showing interrupt messages as session titles
|
||||
- Fixed Opus 4.6 launch announcement showing for Bedrock/Vertex/Foundry users
|
||||
- Improved error message for many-image dimension limit errors with /compact suggestion
|
||||
- Fixed structured-outputs beta header being sent unconditionally on Vertex/Bedrock
|
||||
- Fixed spurious warnings for non-agent markdown files in `.claude/agents/` directory
|
||||
- Improved terminal rendering performance
|
||||
- Fixed fatal errors being swallowed instead of displayed
|
||||
- Fixed process hanging after session close
|
||||
|
||||
173
plugins/security-guidance/hooks/disk_space_utils.py
Normal file
173
plugins/security-guidance/hooks/disk_space_utils.py
Normal file
@@ -0,0 +1,173 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Disk space utilities for Claude Code hooks.
|
||||
|
||||
Provides helper functions to detect and handle disk space issues (ENOSPC errors)
|
||||
in a user-friendly manner.
|
||||
"""
|
||||
|
||||
import errno
|
||||
import os
|
||||
import sys
|
||||
from typing import Optional, Tuple
|
||||
|
||||
|
||||
# ENOSPC errno value (28 on Linux/Mac)
|
||||
ENOSPC_ERRNO = errno.ENOSPC
|
||||
|
||||
|
||||
def is_disk_space_error(exception: Exception) -> bool:
|
||||
"""Check if an exception is related to disk space issues.
|
||||
|
||||
Args:
|
||||
exception: The exception to check
|
||||
|
||||
Returns:
|
||||
True if the exception indicates a disk space issue
|
||||
"""
|
||||
# Check for OSError with ENOSPC errno
|
||||
if isinstance(exception, OSError):
|
||||
if hasattr(exception, 'errno') and exception.errno == ENOSPC_ERRNO:
|
||||
return True
|
||||
# Also check strerror for various disk space error messages
|
||||
if hasattr(exception, 'strerror') and exception.strerror:
|
||||
strerror_lower = exception.strerror.lower()
|
||||
disk_space_indicators = [
|
||||
'no space left on device',
|
||||
'disk quota exceeded',
|
||||
'not enough space',
|
||||
'insufficient disk space',
|
||||
]
|
||||
if any(indicator in strerror_lower for indicator in disk_space_indicators):
|
||||
return True
|
||||
|
||||
# Check error message string as fallback
|
||||
error_str = str(exception).lower()
|
||||
if 'enospc' in error_str or 'no space left' in error_str:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def get_disk_space_warning() -> str:
|
||||
"""Get a user-friendly warning message for disk space issues.
|
||||
|
||||
Returns:
|
||||
Warning message string
|
||||
"""
|
||||
return (
|
||||
"WARNING: Disk space issue detected. Your disk may be full or nearly full.\n"
|
||||
"This can cause Claude Code to become unresponsive or crash.\n"
|
||||
"\n"
|
||||
"Recommended actions:\n"
|
||||
" 1. Free up disk space by deleting unnecessary files\n"
|
||||
" 2. Check available space with: df -h\n"
|
||||
" 3. Clean up temporary files: sudo rm -rf /tmp/* (use with caution)\n"
|
||||
" 4. Empty trash/recycle bin\n"
|
||||
" 5. Consider removing old Docker images: docker system prune"
|
||||
)
|
||||
|
||||
|
||||
def check_available_disk_space(path: str = None, min_bytes: int = 10 * 1024 * 1024) -> Tuple[bool, Optional[str]]:
|
||||
"""Check if there's sufficient disk space available.
|
||||
|
||||
Args:
|
||||
path: Path to check (defaults to home directory)
|
||||
min_bytes: Minimum required bytes (default: 10MB)
|
||||
|
||||
Returns:
|
||||
Tuple of (has_space, warning_message)
|
||||
- has_space: True if sufficient space available
|
||||
- warning_message: Warning string if low on space, None otherwise
|
||||
"""
|
||||
if path is None:
|
||||
path = os.path.expanduser("~")
|
||||
|
||||
try:
|
||||
# Get disk usage statistics
|
||||
stat = os.statvfs(path)
|
||||
available_bytes = stat.f_frsize * stat.f_bavail
|
||||
|
||||
if available_bytes < min_bytes:
|
||||
available_mb = available_bytes / (1024 * 1024)
|
||||
required_mb = min_bytes / (1024 * 1024)
|
||||
return False, (
|
||||
f"Low disk space warning: Only {available_mb:.1f}MB available "
|
||||
f"(recommended minimum: {required_mb:.1f}MB)\n"
|
||||
f"{get_disk_space_warning()}"
|
||||
)
|
||||
|
||||
return True, None
|
||||
|
||||
except (OSError, AttributeError):
|
||||
# os.statvfs not available on all platforms (e.g., Windows)
|
||||
# Return True and let actual write operations fail if there's no space
|
||||
return True, None
|
||||
|
||||
|
||||
def safe_write_file(path: str, content: str, warn_on_disk_error: bool = True) -> Tuple[bool, Optional[str]]:
|
||||
"""Safely write content to a file with disk space error handling.
|
||||
|
||||
Args:
|
||||
path: Path to write to
|
||||
content: Content to write
|
||||
warn_on_disk_error: If True, print warning to stderr on disk space errors
|
||||
|
||||
Returns:
|
||||
Tuple of (success, error_message)
|
||||
- success: True if write succeeded
|
||||
- error_message: Error description if failed, None otherwise
|
||||
"""
|
||||
try:
|
||||
# Ensure directory exists
|
||||
dir_path = os.path.dirname(path)
|
||||
if dir_path:
|
||||
os.makedirs(dir_path, exist_ok=True)
|
||||
|
||||
with open(path, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
return True, None
|
||||
|
||||
except Exception as e:
|
||||
if is_disk_space_error(e):
|
||||
error_msg = f"Disk space error writing to {path}: {e}\n{get_disk_space_warning()}"
|
||||
if warn_on_disk_error:
|
||||
print(error_msg, file=sys.stderr)
|
||||
return False, error_msg
|
||||
else:
|
||||
return False, f"Error writing to {path}: {e}"
|
||||
|
||||
|
||||
def safe_append_file(path: str, content: str, warn_on_disk_error: bool = True) -> Tuple[bool, Optional[str]]:
|
||||
"""Safely append content to a file with disk space error handling.
|
||||
|
||||
Args:
|
||||
path: Path to append to
|
||||
content: Content to append
|
||||
warn_on_disk_error: If True, print warning to stderr on disk space errors
|
||||
|
||||
Returns:
|
||||
Tuple of (success, error_message)
|
||||
- success: True if append succeeded
|
||||
- error_message: Error description if failed, None otherwise
|
||||
"""
|
||||
try:
|
||||
# Ensure directory exists
|
||||
dir_path = os.path.dirname(path)
|
||||
if dir_path:
|
||||
os.makedirs(dir_path, exist_ok=True)
|
||||
|
||||
with open(path, 'a') as f:
|
||||
f.write(content)
|
||||
|
||||
return True, None
|
||||
|
||||
except Exception as e:
|
||||
if is_disk_space_error(e):
|
||||
error_msg = f"Disk space error appending to {path}: {e}\n{get_disk_space_warning()}"
|
||||
if warn_on_disk_error:
|
||||
print(error_msg, file=sys.stderr)
|
||||
return False, error_msg
|
||||
else:
|
||||
return False, f"Error appending to {path}: {e}"
|
||||
@@ -10,18 +10,40 @@ import random
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
# Import disk space utilities
|
||||
try:
|
||||
from disk_space_utils import (
|
||||
is_disk_space_error,
|
||||
get_disk_space_warning,
|
||||
check_available_disk_space,
|
||||
safe_write_file,
|
||||
safe_append_file,
|
||||
)
|
||||
DISK_UTILS_AVAILABLE = True
|
||||
except ImportError:
|
||||
# Fallback if disk_space_utils not available
|
||||
DISK_UTILS_AVAILABLE = False
|
||||
|
||||
# Debug log file
|
||||
DEBUG_LOG_FILE = "/tmp/security-warnings-log.txt"
|
||||
|
||||
# Track if we've already warned about disk space in this session
|
||||
_disk_space_warned = False
|
||||
|
||||
|
||||
def debug_log(message):
|
||||
"""Append debug message to log file with timestamp."""
|
||||
global _disk_space_warned
|
||||
try:
|
||||
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
with open(DEBUG_LOG_FILE, "a") as f:
|
||||
f.write(f"[{timestamp}] {message}\n")
|
||||
except Exception as e:
|
||||
# Silently ignore logging errors to avoid disrupting the hook
|
||||
# Check if this is a disk space error and warn the user
|
||||
if DISK_UTILS_AVAILABLE and is_disk_space_error(e) and not _disk_space_warned:
|
||||
_disk_space_warned = True
|
||||
print(f"[Security Hook] {get_disk_space_warning()}", file=sys.stderr)
|
||||
# Continue silently to avoid disrupting the hook
|
||||
pass
|
||||
|
||||
|
||||
@@ -158,26 +180,44 @@ def cleanup_old_state_files():
|
||||
|
||||
def load_state(session_id):
|
||||
"""Load the state of shown warnings from file."""
|
||||
global _disk_space_warned
|
||||
state_file = get_state_file(session_id)
|
||||
if os.path.exists(state_file):
|
||||
try:
|
||||
with open(state_file, "r") as f:
|
||||
return set(json.load(f))
|
||||
except (json.JSONDecodeError, IOError):
|
||||
except json.JSONDecodeError:
|
||||
debug_log(f"JSON decode error reading state file: {state_file}")
|
||||
return set()
|
||||
except Exception as e:
|
||||
# Check for disk-related errors (corrupted filesystem, etc.)
|
||||
if DISK_UTILS_AVAILABLE and is_disk_space_error(e):
|
||||
if not _disk_space_warned:
|
||||
_disk_space_warned = True
|
||||
print(f"[Security Hook] {get_disk_space_warning()}", file=sys.stderr)
|
||||
debug_log(f"Error loading state file: {e}")
|
||||
return set()
|
||||
return set()
|
||||
|
||||
|
||||
def save_state(session_id, shown_warnings):
|
||||
"""Save the state of shown warnings to file."""
|
||||
global _disk_space_warned
|
||||
state_file = get_state_file(session_id)
|
||||
try:
|
||||
os.makedirs(os.path.dirname(state_file), exist_ok=True)
|
||||
with open(state_file, "w") as f:
|
||||
json.dump(list(shown_warnings), f)
|
||||
except IOError as e:
|
||||
debug_log(f"Failed to save state file: {e}")
|
||||
pass # Fail silently if we can't save state
|
||||
except Exception as e:
|
||||
# Check for disk space errors and provide user-friendly warning
|
||||
if DISK_UTILS_AVAILABLE and is_disk_space_error(e):
|
||||
if not _disk_space_warned:
|
||||
_disk_space_warned = True
|
||||
print(f"[Security Hook] {get_disk_space_warning()}", file=sys.stderr)
|
||||
debug_log(f"Disk space error saving state file: {e}")
|
||||
else:
|
||||
debug_log(f"Failed to save state file: {e}")
|
||||
# Fail silently to not disrupt operation
|
||||
|
||||
|
||||
def check_patterns(file_path, content):
|
||||
@@ -216,6 +256,8 @@ def extract_content_from_input(tool_name, tool_input):
|
||||
|
||||
def main():
|
||||
"""Main hook function."""
|
||||
global _disk_space_warned
|
||||
|
||||
# Check if security reminders are enabled
|
||||
security_reminder_enabled = os.environ.get("ENABLE_SECURITY_REMINDER", "1")
|
||||
|
||||
@@ -223,6 +265,13 @@ def main():
|
||||
if security_reminder_enabled == "0":
|
||||
sys.exit(0)
|
||||
|
||||
# Check for low disk space and warn user (only once per session)
|
||||
if DISK_UTILS_AVAILABLE and not _disk_space_warned:
|
||||
has_space, warning = check_available_disk_space()
|
||||
if not has_space:
|
||||
_disk_space_warned = True
|
||||
print(f"[Security Hook] {warning}", file=sys.stderr)
|
||||
|
||||
# Periodically clean up old state files (10% chance per run)
|
||||
if random.random() < 0.1:
|
||||
cleanup_old_state_files()
|
||||
|
||||
@@ -1,87 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Edits labels on a GitHub issue.
|
||||
# Usage: ./edit-issue-labels.sh --issue 123 --add-label bug --add-label needs-triage --remove-label untriaged
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
ISSUE=""
|
||||
ADD_LABELS=()
|
||||
REMOVE_LABELS=()
|
||||
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--issue)
|
||||
ISSUE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--add-label)
|
||||
ADD_LABELS+=("$2")
|
||||
shift 2
|
||||
;;
|
||||
--remove-label)
|
||||
REMOVE_LABELS+=("$2")
|
||||
shift 2
|
||||
;;
|
||||
*)
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate issue number
|
||||
if [[ -z "$ISSUE" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! [[ "$ISSUE" =~ ^[0-9]+$ ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ ${#ADD_LABELS[@]} -eq 0 && ${#REMOVE_LABELS[@]} -eq 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Fetch valid labels from the repo
|
||||
VALID_LABELS=$(gh label list --limit 500 --json name --jq '.[].name')
|
||||
|
||||
# Filter to only labels that exist in the repo
|
||||
FILTERED_ADD=()
|
||||
for label in "${ADD_LABELS[@]}"; do
|
||||
if echo "$VALID_LABELS" | grep -qxF "$label"; then
|
||||
FILTERED_ADD+=("$label")
|
||||
fi
|
||||
done
|
||||
|
||||
FILTERED_REMOVE=()
|
||||
for label in "${REMOVE_LABELS[@]}"; do
|
||||
if echo "$VALID_LABELS" | grep -qxF "$label"; then
|
||||
FILTERED_REMOVE+=("$label")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ ${#FILTERED_ADD[@]} -eq 0 && ${#FILTERED_REMOVE[@]} -eq 0 ]]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Build gh command arguments
|
||||
GH_ARGS=("issue" "edit" "$ISSUE")
|
||||
|
||||
for label in "${FILTERED_ADD[@]}"; do
|
||||
GH_ARGS+=("--add-label" "$label")
|
||||
done
|
||||
|
||||
for label in "${FILTERED_REMOVE[@]}"; do
|
||||
GH_ARGS+=("--remove-label" "$label")
|
||||
done
|
||||
|
||||
gh "${GH_ARGS[@]}"
|
||||
|
||||
if [[ ${#FILTERED_ADD[@]} -gt 0 ]]; then
|
||||
echo "Added: ${FILTERED_ADD[*]}"
|
||||
fi
|
||||
if [[ ${#FILTERED_REMOVE[@]} -gt 0 ]]; then
|
||||
echo "Removed: ${FILTERED_REMOVE[*]}"
|
||||
fi
|
||||
@@ -1,96 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Wrapper around gh CLI that only allows specific subcommands and flags.
|
||||
# All commands are scoped to the current repository via GH_REPO or GITHUB_REPOSITORY.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/gh.sh issue view 123
|
||||
# ./scripts/gh.sh issue view 123 --comments
|
||||
# ./scripts/gh.sh issue list --state open --limit 20
|
||||
# ./scripts/gh.sh search issues "search query" --limit 10
|
||||
# ./scripts/gh.sh label list --limit 100
|
||||
|
||||
export GH_HOST=github.com
|
||||
|
||||
REPO="${GH_REPO:-${GITHUB_REPOSITORY:-}}"
|
||||
if [[ -z "$REPO" || "$REPO" == */*/* || "$REPO" != */* ]]; then
|
||||
echo "Error: GH_REPO or GITHUB_REPOSITORY must be set to owner/repo format (e.g., GITHUB_REPOSITORY=anthropics/claude-code)" >&2
|
||||
exit 1
|
||||
fi
|
||||
export GH_REPO="$REPO"
|
||||
|
||||
ALLOWED_FLAGS=(--comments --state --limit --label)
|
||||
FLAGS_WITH_VALUES=(--state --limit --label)
|
||||
|
||||
SUB1="${1:-}"
|
||||
SUB2="${2:-}"
|
||||
CMD="$SUB1 $SUB2"
|
||||
case "$CMD" in
|
||||
"issue view"|"issue list"|"search issues"|"label list")
|
||||
;;
|
||||
*)
|
||||
echo "Error: only 'issue view', 'issue list', 'search issues', 'label list' are allowed (e.g., ./scripts/gh.sh issue view 123)" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
shift 2
|
||||
|
||||
# Separate flags from positional arguments
|
||||
POSITIONAL=()
|
||||
FLAGS=()
|
||||
skip_next=false
|
||||
for arg in "$@"; do
|
||||
if [[ "$skip_next" == true ]]; then
|
||||
FLAGS+=("$arg")
|
||||
skip_next=false
|
||||
elif [[ "$arg" == -* ]]; then
|
||||
flag="${arg%%=*}"
|
||||
matched=false
|
||||
for allowed in "${ALLOWED_FLAGS[@]}"; do
|
||||
if [[ "$flag" == "$allowed" ]]; then
|
||||
matched=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ "$matched" == false ]]; then
|
||||
echo "Error: only --comments, --state, --limit, --label flags are allowed (e.g., ./scripts/gh.sh issue list --state open --limit 20)" >&2
|
||||
exit 1
|
||||
fi
|
||||
FLAGS+=("$arg")
|
||||
# If flag expects a value and isn't using = syntax, skip next arg
|
||||
if [[ "$arg" != *=* ]]; then
|
||||
for vflag in "${FLAGS_WITH_VALUES[@]}"; do
|
||||
if [[ "$flag" == "$vflag" ]]; then
|
||||
skip_next=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
else
|
||||
POSITIONAL+=("$arg")
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$CMD" == "search issues" ]]; then
|
||||
QUERY="${POSITIONAL[0]:-}"
|
||||
QUERY_LOWER=$(echo "$QUERY" | tr '[:upper:]' '[:lower:]')
|
||||
if [[ "$QUERY_LOWER" == *"repo:"* || "$QUERY_LOWER" == *"org:"* || "$QUERY_LOWER" == *"user:"* ]]; then
|
||||
echo "Error: search query must not contain repo:, org:, or user: qualifiers (e.g., ./scripts/gh.sh search issues \"bug report\" --limit 10)" >&2
|
||||
exit 1
|
||||
fi
|
||||
gh "$SUB1" "$SUB2" "$QUERY" --repo "$REPO" "${FLAGS[@]}"
|
||||
elif [[ "$CMD" == "issue view" ]]; then
|
||||
if [[ ${#POSITIONAL[@]} -ne 1 ]] || ! [[ "${POSITIONAL[0]}" =~ ^[0-9]+$ ]]; then
|
||||
echo "Error: issue view requires exactly one numeric issue number (e.g., ./scripts/gh.sh issue view 123)" >&2
|
||||
exit 1
|
||||
fi
|
||||
gh "$SUB1" "$SUB2" "${POSITIONAL[0]}" "${FLAGS[@]}"
|
||||
else
|
||||
if [[ ${#POSITIONAL[@]} -ne 0 ]]; then
|
||||
echo "Error: issue list and label list do not accept positional arguments (e.g., ./scripts/gh.sh issue list --state open, ./scripts/gh.sh label list --limit 100)" >&2
|
||||
exit 1
|
||||
fi
|
||||
gh "$SUB1" "$SUB2" "${FLAGS[@]}"
|
||||
fi
|
||||
@@ -1,38 +0,0 @@
|
||||
// Single source of truth for issue lifecycle labels, timeouts, and messages.
|
||||
|
||||
export const lifecycle = [
|
||||
{
|
||||
label: "invalid",
|
||||
days: 3,
|
||||
reason: "this doesn't appear to be about Claude Code",
|
||||
nudge: "This doesn't appear to be about [Claude Code](https://github.com/anthropics/claude-code). For general Anthropic support, visit [support.anthropic.com](https://support.anthropic.com).",
|
||||
},
|
||||
{
|
||||
label: "needs-repro",
|
||||
days: 7,
|
||||
reason: "we still need reproduction steps to investigate",
|
||||
nudge: "We weren't able to reproduce this. Could you provide steps to trigger the issue — what you ran, what happened, and what you expected?",
|
||||
},
|
||||
{
|
||||
label: "needs-info",
|
||||
days: 7,
|
||||
reason: "we still need a bit more information to move forward",
|
||||
nudge: "We need more information to continue investigating. Can you make sure to include your Claude Code version (`claude --version`), OS, and any error messages or logs?",
|
||||
},
|
||||
{
|
||||
label: "stale",
|
||||
days: 14,
|
||||
reason: "inactive for too long",
|
||||
nudge: "This issue has been automatically marked as stale due to inactivity.",
|
||||
},
|
||||
{
|
||||
label: "autoclose",
|
||||
days: 14,
|
||||
reason: "inactive for too long",
|
||||
nudge: "This issue has been marked for automatic closure.",
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type LifecycleLabel = (typeof lifecycle)[number]["label"];
|
||||
|
||||
export const STALE_UPVOTE_THRESHOLD = 10;
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
// Posts a comment when a lifecycle label is applied to an issue,
|
||||
// giving the author a heads-up and a chance to respond before auto-close.
|
||||
|
||||
import { lifecycle } from "./issue-lifecycle.ts";
|
||||
|
||||
const DRY_RUN = process.argv.includes("--dry-run");
|
||||
const token = process.env.GITHUB_TOKEN;
|
||||
const repo = process.env.GITHUB_REPOSITORY; // owner/repo
|
||||
const label = process.env.LABEL;
|
||||
const issueNumber = process.env.ISSUE_NUMBER;
|
||||
|
||||
if (!DRY_RUN && !token) throw new Error("GITHUB_TOKEN required");
|
||||
if (!repo) throw new Error("GITHUB_REPOSITORY required");
|
||||
if (!label) throw new Error("LABEL required");
|
||||
if (!issueNumber) throw new Error("ISSUE_NUMBER required");
|
||||
|
||||
const entry = lifecycle.find((l) => l.label === label);
|
||||
if (!entry) {
|
||||
console.log(`No lifecycle entry for label "${label}", skipping`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const body = `${entry.nudge} This issue will be closed automatically if there's no activity within ${entry.days} days.`;
|
||||
|
||||
// --
|
||||
|
||||
if (DRY_RUN) {
|
||||
console.log(`Would comment on #${issueNumber} for label "${label}":\n\n${body}`);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`https://api.github.com/repos/${repo}/issues/${issueNumber}/comments`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
Accept: "application/vnd.github.v3+json",
|
||||
"Content-Type": "application/json",
|
||||
"User-Agent": "lifecycle-comment",
|
||||
},
|
||||
body: JSON.stringify({ body }),
|
||||
}
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
const text = await response.text();
|
||||
throw new Error(`GitHub API ${response.status}: ${text}`);
|
||||
}
|
||||
|
||||
console.log(`Commented on #${issueNumber} for label "${label}"`);
|
||||
@@ -1,15 +1,23 @@
|
||||
#!/usr/bin/env bun
|
||||
|
||||
import { lifecycle, STALE_UPVOTE_THRESHOLD } from "./issue-lifecycle.ts";
|
||||
|
||||
// --
|
||||
|
||||
const NEW_ISSUE = "https://github.com/anthropics/claude-code/issues/new/choose";
|
||||
const DRY_RUN = process.argv.includes("--dry-run");
|
||||
const STALE_DAYS = 14;
|
||||
const STALE_UPVOTE_THRESHOLD = 10;
|
||||
|
||||
const CLOSE_MESSAGE = (reason: string) =>
|
||||
`Closing for now — ${reason}. Please [open a new issue](${NEW_ISSUE}) if this is still relevant.`;
|
||||
|
||||
const lifecycle = [
|
||||
{ label: "invalid", days: 3, reason: "this doesn't appear to be about Claude Code" },
|
||||
{ label: "needs-repro", days: 7, reason: "we still need reproduction steps to investigate" },
|
||||
{ label: "needs-info", days: 7, reason: "we still need a bit more information to move forward" },
|
||||
{ label: "stale", days: 14, reason: "inactive for too long" },
|
||||
{ label: "autoclose", days: 14, reason: "inactive for too long" },
|
||||
];
|
||||
|
||||
// --
|
||||
|
||||
async function githubRequest<T>(
|
||||
@@ -43,13 +51,12 @@ async function githubRequest<T>(
|
||||
// --
|
||||
|
||||
async function markStale(owner: string, repo: string) {
|
||||
const staleDays = lifecycle.find((l) => l.label === "stale")!.days;
|
||||
const cutoff = new Date();
|
||||
cutoff.setDate(cutoff.getDate() - staleDays);
|
||||
cutoff.setDate(cutoff.getDate() - STALE_DAYS);
|
||||
|
||||
let labeled = 0;
|
||||
|
||||
console.log(`\n=== marking stale (${staleDays}d inactive) ===`);
|
||||
console.log(`\n=== marking stale (${STALE_DAYS}d inactive) ===`);
|
||||
|
||||
for (let page = 1; page <= 10; page++) {
|
||||
const issues = await githubRequest<any[]>(
|
||||
@@ -70,8 +77,11 @@ async function markStale(owner: string, repo: string) {
|
||||
);
|
||||
if (alreadyStale) continue;
|
||||
|
||||
const isEnhancement = issue.labels?.some(
|
||||
(l: any) => l.name === "enhancement"
|
||||
);
|
||||
const thumbsUp = issue.reactions?.["+1"] ?? 0;
|
||||
if (thumbsUp >= STALE_UPVOTE_THRESHOLD) continue;
|
||||
if (isEnhancement && thumbsUp >= STALE_UPVOTE_THRESHOLD) continue;
|
||||
|
||||
const base = `/repos/${owner}/${repo}/issues/${issue.number}`;
|
||||
|
||||
@@ -105,11 +115,6 @@ async function closeExpired(owner: string, repo: string) {
|
||||
|
||||
for (const issue of issues) {
|
||||
if (issue.pull_request) continue;
|
||||
if (issue.locked) continue;
|
||||
|
||||
const thumbsUp = issue.reactions?.["+1"] ?? 0;
|
||||
if (thumbsUp >= STALE_UPVOTE_THRESHOLD) continue;
|
||||
|
||||
const base = `/repos/${owner}/${repo}/issues/${issue.number}`;
|
||||
|
||||
const events = await githubRequest<any[]>(`${base}/events?per_page=100`);
|
||||
@@ -121,22 +126,6 @@ async function closeExpired(owner: string, repo: string) {
|
||||
|
||||
if (!labeledAt || labeledAt > cutoff) continue;
|
||||
|
||||
// Skip if a non-bot user commented after the label was applied.
|
||||
// The triage workflow should remove lifecycle labels on human
|
||||
// activity, but check here too as a safety net.
|
||||
const comments = await githubRequest<any[]>(
|
||||
`${base}/comments?since=${labeledAt.toISOString()}&per_page=100`
|
||||
);
|
||||
const hasHumanComment = comments.some(
|
||||
(c) => c.user && c.user.type !== "Bot"
|
||||
);
|
||||
if (hasHumanComment) {
|
||||
console.log(
|
||||
`#${issue.number}: skipping (human activity after ${label} label)`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DRY_RUN) {
|
||||
const age = Math.floor((Date.now() - labeledAt.getTime()) / 86400000);
|
||||
console.log(`#${issue.number}: would close (${label}, ${age}d old) — ${issue.title}`);
|
||||
@@ -155,14 +144,20 @@ async function closeExpired(owner: string, repo: string) {
|
||||
|
||||
// --
|
||||
|
||||
const owner = process.env.GITHUB_REPOSITORY_OWNER;
|
||||
const repo = process.env.GITHUB_REPOSITORY_NAME;
|
||||
if (!owner || !repo)
|
||||
throw new Error("GITHUB_REPOSITORY_OWNER and GITHUB_REPOSITORY_NAME required");
|
||||
async function main() {
|
||||
const owner = process.env.GITHUB_REPOSITORY_OWNER;
|
||||
const repo = process.env.GITHUB_REPOSITORY_NAME;
|
||||
if (!owner || !repo)
|
||||
throw new Error("GITHUB_REPOSITORY_OWNER and GITHUB_REPOSITORY_NAME required");
|
||||
|
||||
if (DRY_RUN) console.log("DRY RUN — no changes will be made\n");
|
||||
if (DRY_RUN) console.log("DRY RUN — no changes will be made\n");
|
||||
|
||||
const labeled = await markStale(owner, repo);
|
||||
const closed = await closeExpired(owner, repo);
|
||||
const labeled = await markStale(owner, repo);
|
||||
const closed = await closeExpired(owner, repo);
|
||||
|
||||
console.log(`\nDone: ${labeled} ${DRY_RUN ? "would be labeled" : "labeled"} stale, ${closed} ${DRY_RUN ? "would be closed" : "closed"}`);
|
||||
console.log(`\nDone: ${labeled} ${DRY_RUN ? "would be labeled" : "labeled"} stale, ${closed} ${DRY_RUN ? "would be closed" : "closed"}`);
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
|
||||
export {};
|
||||
|
||||
Reference in New Issue
Block a user