mirror of
https://github.com/anthropics/claude-code.git
synced 2026-04-19 01:52:42 +00:00
Compare commits
13 Commits
claude/sla
...
claude/sla
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e1f4306d1 | ||
|
|
d5912a011d | ||
|
|
52115592ba | ||
|
|
5d2df70860 | ||
|
|
b8a2ffb38f | ||
|
|
9fd556d947 | ||
|
|
b31e2fd182 | ||
|
|
d2cb503247 | ||
|
|
5fe61207ff | ||
|
|
1ed82e6af0 | ||
|
|
2a61cb364c | ||
|
|
6880bcbace | ||
|
|
4392352687 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,2 @@
|
||||
.DS_Store
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
|
||||
36
CHANGELOG.md
36
CHANGELOG.md
@@ -1,11 +1,45 @@
|
||||
# Changelog
|
||||
|
||||
## 2.0.73
|
||||
|
||||
- Added clickable `[Image #N]` links that open attached images in the default viewer
|
||||
- Added alt-y yank-pop to cycle through kill ring history after ctrl-y yank
|
||||
- Added search filtering to the plugin discover screen (type to filter by name, description, or marketplace)
|
||||
- Added support for custom session IDs when forking sessions with `--session-id` combined with `--resume` or `--continue` and `--fork-session`
|
||||
- Fixed slow input history cycling and race condition that could overwrite text after message submission
|
||||
- Improved `/theme` command to open theme picker directly
|
||||
- Improved theme picker UI
|
||||
- Improved search UX across resume session, permissions, and plugins screens with a unified SearchBox component
|
||||
- [VSCode] Added tab icon badges showing pending permissions (blue) and unread completions (orange)
|
||||
|
||||
## 2.0.72
|
||||
|
||||
- Added Claude in Chrome (Beta) feature that works with the Chrome extension (https://claude.ai/chrome) to let you control your browser directly from Claude Code
|
||||
- Reduced terminal flickering
|
||||
- Added scannable QR code to mobile app tip for quick app downloads
|
||||
- Added loading indicator when resuming conversations for better feedback
|
||||
- Fixed `/context` command not respecting custom system prompts in non-interactive mode
|
||||
- Fixed order of consecutive Ctrl+K lines when pasting with Ctrl+Y
|
||||
- Improved @ mention file suggestion speed (~3x faster in git repositories)
|
||||
- Improved file suggestion performance in repos with `.ignore` or `.rgignore` files
|
||||
- Improved settings validation errors to be more prominent
|
||||
- Changed thinking toggle from Tab to Alt+T to avoid accidental triggers
|
||||
|
||||
## 2.0.71
|
||||
|
||||
- Added /config toggle to enable/disable prompt suggestions
|
||||
- Added `/settings` as an alias for the `/config` command
|
||||
- Fixed @ file reference suggestions incorrectly triggering when cursor is in the middle of a path
|
||||
- Fixed MCP servers from `.mcp.json` not loading when using `--dangerously-skip-permissions`
|
||||
- Fixed permission rules incorrectly rejecting valid bash commands containing shell glob patterns (e.g., `ls *.txt`, `for f in *.png`)
|
||||
- Bedrock: Environment variable `ANTHROPIC_BEDROCK_BASE_URL` is now respected for token counting and inference profile listing
|
||||
- New syntax highlighting engine for native build
|
||||
|
||||
## 2.0.70
|
||||
|
||||
- Added Enter key to accept and submit prompt suggestions immediately (tab still accepts for editing)
|
||||
- Added wildcard syntax `mcp__server__*` for MCP tool permissions to allow or deny all tools from a server
|
||||
- Added auto-update toggle for plugin marketplaces, allowing per-marketplace control over automatic updates
|
||||
- Added `plan_mode_required` spawn parameter for teammates to require plan approval before implementing changes
|
||||
- Added `current_usage` field to status line input, enabling accurate context window percentage calculations
|
||||
- Fixed input being cleared when processing queued commands while the user was typing
|
||||
- Fixed prompt suggestions replacing typed input when pressing Tab
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Claude Code Hook: Fix File Permissions After Write
|
||||
===================================================
|
||||
This hook runs as a PostToolUse hook for the Write and Edit tools.
|
||||
It fixes file permissions to respect the system's umask setting.
|
||||
|
||||
This addresses the issue where Claude Code's Write tool creates files with
|
||||
restrictive 0600 permissions, ignoring the user's umask setting.
|
||||
|
||||
Read more about hooks here: https://docs.anthropic.com/en/docs/claude-code/hooks
|
||||
|
||||
Configuration example for ~/.claude/settings.json or .claude/settings.local.json:
|
||||
|
||||
{
|
||||
"hooks": {
|
||||
"PostToolUse": [
|
||||
{
|
||||
"matcher": "Write|Edit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "python3 /path/to/claude-code/examples/hooks/fix_file_permissions_example.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
How it works:
|
||||
- After Write or Edit tool completes, this hook runs
|
||||
- Gets the file path from the tool input
|
||||
- Calculates the correct permissions based on the current umask
|
||||
- Applies the umask-respecting permissions to the file
|
||||
|
||||
For example:
|
||||
- With umask 022: files become 0644 (rw-r--r--)
|
||||
- With umask 002: files become 0664 (rw-rw-r--)
|
||||
- With umask 077: files remain 0600 (rw-------)
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import stat
|
||||
import sys
|
||||
|
||||
|
||||
def get_umask() -> int:
|
||||
"""Get the current umask value.
|
||||
|
||||
We temporarily set umask to get the current value, then restore it.
|
||||
This is the standard way to read umask in Python.
|
||||
"""
|
||||
current_umask = os.umask(0)
|
||||
os.umask(current_umask)
|
||||
return current_umask
|
||||
|
||||
|
||||
def calculate_file_permissions(umask_value: int) -> int:
|
||||
"""Calculate file permissions based on umask.
|
||||
|
||||
Standard Unix behavior: new files start with 0666 base permissions,
|
||||
then umask is applied to remove bits.
|
||||
|
||||
Args:
|
||||
umask_value: The current umask value (e.g., 0o022)
|
||||
|
||||
Returns:
|
||||
The file permissions after applying umask (e.g., 0o644)
|
||||
"""
|
||||
base_permissions = 0o666 # rw-rw-rw-
|
||||
return base_permissions & ~umask_value
|
||||
|
||||
|
||||
def fix_file_permissions(file_path: str) -> dict:
|
||||
"""Fix permissions for a file to respect umask.
|
||||
|
||||
Args:
|
||||
file_path: Path to the file to fix
|
||||
|
||||
Returns:
|
||||
Dict with status information
|
||||
"""
|
||||
if not file_path:
|
||||
return {"status": "skipped", "reason": "no file path provided"}
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
return {"status": "skipped", "reason": "file does not exist"}
|
||||
|
||||
if not os.path.isfile(file_path):
|
||||
return {"status": "skipped", "reason": "path is not a file"}
|
||||
|
||||
try:
|
||||
# Get current permissions
|
||||
current_mode = stat.S_IMODE(os.stat(file_path).st_mode)
|
||||
|
||||
# Calculate expected permissions based on umask
|
||||
umask_value = get_umask()
|
||||
expected_mode = calculate_file_permissions(umask_value)
|
||||
|
||||
# Only change if current permissions are more restrictive than expected
|
||||
# This handles the case where Write tool sets 0600 instead of umask-based perms
|
||||
if current_mode == 0o600 and expected_mode != 0o600:
|
||||
os.chmod(file_path, expected_mode)
|
||||
return {
|
||||
"status": "fixed",
|
||||
"file": file_path,
|
||||
"old_mode": oct(current_mode),
|
||||
"new_mode": oct(expected_mode),
|
||||
"umask": oct(umask_value),
|
||||
}
|
||||
else:
|
||||
return {
|
||||
"status": "unchanged",
|
||||
"file": file_path,
|
||||
"current_mode": oct(current_mode),
|
||||
"expected_mode": oct(expected_mode),
|
||||
}
|
||||
|
||||
except PermissionError as e:
|
||||
return {"status": "error", "reason": f"permission denied: {e}"}
|
||||
except OSError as e:
|
||||
return {"status": "error", "reason": f"OS error: {e}"}
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for the PostToolUse hook."""
|
||||
try:
|
||||
input_data = json.load(sys.stdin)
|
||||
except json.JSONDecodeError as e:
|
||||
# Exit 0 - don't block on invalid input, just log to stderr
|
||||
print(f"Warning: Invalid JSON input: {e}", file=sys.stderr)
|
||||
sys.exit(0)
|
||||
|
||||
tool_name = input_data.get("tool_name", "")
|
||||
|
||||
# Only process Write and Edit tools
|
||||
if tool_name not in ("Write", "Edit"):
|
||||
sys.exit(0)
|
||||
|
||||
tool_input = input_data.get("tool_input", {})
|
||||
file_path = tool_input.get("file_path", "")
|
||||
|
||||
if not file_path:
|
||||
sys.exit(0)
|
||||
|
||||
result = fix_file_permissions(file_path)
|
||||
|
||||
# Output result as JSON for logging/debugging
|
||||
# This will appear in the transcript when running with --debug
|
||||
if result.get("status") == "fixed":
|
||||
output = {
|
||||
"systemMessage": f"Fixed file permissions for {file_path}: {result['old_mode']} -> {result['new_mode']} (umask: {result['umask']})"
|
||||
}
|
||||
print(json.dumps(output))
|
||||
|
||||
# Always exit 0 - this is a PostToolUse hook, we don't want to block
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,278 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Tests for the fix_file_permissions_example.py hook.
|
||||
|
||||
Run these tests with:
|
||||
python3 examples/hooks/test_fix_file_permissions.py
|
||||
|
||||
Or with pytest:
|
||||
pytest examples/hooks/test_fix_file_permissions.py -v
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
# Path to the hook script
|
||||
HOOK_SCRIPT = Path(__file__).parent / "fix_file_permissions_example.py"
|
||||
|
||||
|
||||
class TestFixFilePermissionsHook(unittest.TestCase):
|
||||
"""Test cases for the file permissions fix hook."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up test fixtures."""
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.test_file = os.path.join(self.temp_dir, "test_file.txt")
|
||||
|
||||
def tearDown(self):
|
||||
"""Clean up test files."""
|
||||
if os.path.exists(self.test_file):
|
||||
os.remove(self.test_file)
|
||||
if os.path.exists(self.temp_dir):
|
||||
os.rmdir(self.temp_dir)
|
||||
|
||||
def run_hook(self, tool_name: str, file_path: str) -> subprocess.CompletedProcess:
|
||||
"""Run the hook script with given input."""
|
||||
input_data = {
|
||||
"tool_name": tool_name,
|
||||
"tool_input": {"file_path": file_path},
|
||||
"session_id": "test-session",
|
||||
"cwd": os.getcwd(),
|
||||
}
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(HOOK_SCRIPT)],
|
||||
input=json.dumps(input_data),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
return result
|
||||
|
||||
def create_file_with_permissions(self, path: str, mode: int) -> None:
|
||||
"""Create a test file with specific permissions."""
|
||||
with open(path, "w") as f:
|
||||
f.write("test content")
|
||||
os.chmod(path, mode)
|
||||
|
||||
def get_file_permissions(self, path: str) -> int:
|
||||
"""Get the permission bits of a file."""
|
||||
return stat.S_IMODE(os.stat(path).st_mode)
|
||||
|
||||
def test_fixes_restrictive_permissions_with_umask_022(self):
|
||||
"""Test that 0600 permissions are fixed to 0644 with umask 022."""
|
||||
# Save and set umask
|
||||
old_umask = os.umask(0o022)
|
||||
try:
|
||||
# Create file with restrictive permissions (simulating Write tool bug)
|
||||
self.create_file_with_permissions(self.test_file, 0o600)
|
||||
self.assertEqual(self.get_file_permissions(self.test_file), 0o600)
|
||||
|
||||
# Run the hook
|
||||
result = self.run_hook("Write", self.test_file)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
|
||||
# Check permissions were fixed
|
||||
expected_mode = 0o644 # 0666 & ~0022
|
||||
self.assertEqual(
|
||||
self.get_file_permissions(self.test_file),
|
||||
expected_mode,
|
||||
f"Expected {oct(expected_mode)}, got {oct(self.get_file_permissions(self.test_file))}",
|
||||
)
|
||||
finally:
|
||||
os.umask(old_umask)
|
||||
|
||||
def test_fixes_restrictive_permissions_with_umask_002(self):
|
||||
"""Test that 0600 permissions are fixed to 0664 with umask 002."""
|
||||
# Save and set umask
|
||||
old_umask = os.umask(0o002)
|
||||
try:
|
||||
# Create file with restrictive permissions
|
||||
self.create_file_with_permissions(self.test_file, 0o600)
|
||||
self.assertEqual(self.get_file_permissions(self.test_file), 0o600)
|
||||
|
||||
# Run the hook
|
||||
result = self.run_hook("Write", self.test_file)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
|
||||
# Check permissions were fixed
|
||||
expected_mode = 0o664 # 0666 & ~0002
|
||||
self.assertEqual(
|
||||
self.get_file_permissions(self.test_file),
|
||||
expected_mode,
|
||||
f"Expected {oct(expected_mode)}, got {oct(self.get_file_permissions(self.test_file))}",
|
||||
)
|
||||
finally:
|
||||
os.umask(old_umask)
|
||||
|
||||
def test_preserves_permissions_matching_umask(self):
|
||||
"""Test that permissions already matching umask are not changed."""
|
||||
old_umask = os.umask(0o022)
|
||||
try:
|
||||
# Create file with correct permissions already
|
||||
self.create_file_with_permissions(self.test_file, 0o644)
|
||||
|
||||
# Run the hook
|
||||
result = self.run_hook("Write", self.test_file)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
|
||||
# Permissions should be unchanged
|
||||
self.assertEqual(self.get_file_permissions(self.test_file), 0o644)
|
||||
finally:
|
||||
os.umask(old_umask)
|
||||
|
||||
def test_respects_umask_077(self):
|
||||
"""Test that umask 077 results in 0600 (no change needed)."""
|
||||
old_umask = os.umask(0o077)
|
||||
try:
|
||||
# Create file with 0600 permissions
|
||||
self.create_file_with_permissions(self.test_file, 0o600)
|
||||
|
||||
# Run the hook
|
||||
result = self.run_hook("Write", self.test_file)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
|
||||
# With umask 077, 0600 is correct - should remain unchanged
|
||||
self.assertEqual(self.get_file_permissions(self.test_file), 0o600)
|
||||
finally:
|
||||
os.umask(old_umask)
|
||||
|
||||
def test_handles_edit_tool(self):
|
||||
"""Test that the hook also works for the Edit tool."""
|
||||
old_umask = os.umask(0o022)
|
||||
try:
|
||||
self.create_file_with_permissions(self.test_file, 0o600)
|
||||
|
||||
result = self.run_hook("Edit", self.test_file)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
|
||||
self.assertEqual(self.get_file_permissions(self.test_file), 0o644)
|
||||
finally:
|
||||
os.umask(old_umask)
|
||||
|
||||
def test_ignores_other_tools(self):
|
||||
"""Test that the hook ignores non-Write/Edit tools."""
|
||||
old_umask = os.umask(0o022)
|
||||
try:
|
||||
self.create_file_with_permissions(self.test_file, 0o600)
|
||||
|
||||
result = self.run_hook("Read", self.test_file)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
|
||||
# Permissions should be unchanged for Read tool
|
||||
self.assertEqual(self.get_file_permissions(self.test_file), 0o600)
|
||||
finally:
|
||||
os.umask(old_umask)
|
||||
|
||||
def test_handles_nonexistent_file(self):
|
||||
"""Test that the hook handles non-existent files gracefully."""
|
||||
result = self.run_hook("Write", "/nonexistent/path/file.txt")
|
||||
self.assertEqual(result.returncode, 0)
|
||||
|
||||
def test_handles_empty_file_path(self):
|
||||
"""Test that the hook handles empty file path gracefully."""
|
||||
input_data = {
|
||||
"tool_name": "Write",
|
||||
"tool_input": {},
|
||||
"session_id": "test-session",
|
||||
}
|
||||
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(HOOK_SCRIPT)],
|
||||
input=json.dumps(input_data),
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
|
||||
def test_handles_invalid_json(self):
|
||||
"""Test that the hook handles invalid JSON input gracefully."""
|
||||
result = subprocess.run(
|
||||
[sys.executable, str(HOOK_SCRIPT)],
|
||||
input="not valid json",
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
# Should exit 0 even with invalid input (don't block the workflow)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
self.assertIn("Invalid JSON", result.stderr)
|
||||
|
||||
def test_handles_directory_path(self):
|
||||
"""Test that the hook ignores directory paths."""
|
||||
result = self.run_hook("Write", self.temp_dir)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
|
||||
def test_outputs_system_message_on_fix(self):
|
||||
"""Test that the hook outputs a systemMessage when fixing permissions."""
|
||||
old_umask = os.umask(0o022)
|
||||
try:
|
||||
self.create_file_with_permissions(self.test_file, 0o600)
|
||||
|
||||
result = self.run_hook("Write", self.test_file)
|
||||
self.assertEqual(result.returncode, 0)
|
||||
|
||||
# Check that stdout contains the systemMessage JSON
|
||||
if result.stdout.strip():
|
||||
output = json.loads(result.stdout)
|
||||
self.assertIn("systemMessage", output)
|
||||
self.assertIn("Fixed file permissions", output["systemMessage"])
|
||||
finally:
|
||||
os.umask(old_umask)
|
||||
|
||||
|
||||
class TestCalculateFilePermissions(unittest.TestCase):
|
||||
"""Test the calculate_file_permissions function directly."""
|
||||
|
||||
def test_umask_022(self):
|
||||
"""Test permission calculation with umask 022."""
|
||||
# Import the function from the hook script
|
||||
import importlib.util
|
||||
|
||||
spec = importlib.util.spec_from_file_location("hook", HOOK_SCRIPT)
|
||||
hook = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(hook)
|
||||
|
||||
result = hook.calculate_file_permissions(0o022)
|
||||
self.assertEqual(result, 0o644)
|
||||
|
||||
def test_umask_002(self):
|
||||
"""Test permission calculation with umask 002."""
|
||||
import importlib.util
|
||||
|
||||
spec = importlib.util.spec_from_file_location("hook", HOOK_SCRIPT)
|
||||
hook = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(hook)
|
||||
|
||||
result = hook.calculate_file_permissions(0o002)
|
||||
self.assertEqual(result, 0o664)
|
||||
|
||||
def test_umask_077(self):
|
||||
"""Test permission calculation with umask 077."""
|
||||
import importlib.util
|
||||
|
||||
spec = importlib.util.spec_from_file_location("hook", HOOK_SCRIPT)
|
||||
hook = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(hook)
|
||||
|
||||
result = hook.calculate_file_permissions(0o077)
|
||||
self.assertEqual(result, 0o600)
|
||||
|
||||
def test_umask_000(self):
|
||||
"""Test permission calculation with umask 000."""
|
||||
import importlib.util
|
||||
|
||||
spec = importlib.util.spec_from_file_location("hook", HOOK_SCRIPT)
|
||||
hook = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(hook)
|
||||
|
||||
result = hook.calculate_file_permissions(0o000)
|
||||
self.assertEqual(result, 0o666)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh pr comment:*), Bash(gh pr diff:*), Bash(gh pr view:*), Bash(gh pr list:*)
|
||||
allowed-tools: Bash(gh issue view:*), Bash(gh search:*), Bash(gh issue list:*), Bash(gh pr comment:*), Bash(gh pr diff:*), Bash(gh pr view:*), Bash(gh pr list:*), mcp__github_inline_comment__create_inline_comment
|
||||
description: Code review a pull request
|
||||
---
|
||||
|
||||
@@ -11,7 +11,7 @@ To do this, follow these steps precisely:
|
||||
- The pull request is closed
|
||||
- The pull request is a draft
|
||||
- The pull request does not need code review (e.g. automated PR, trivial change that is obviously correct)
|
||||
- You have already submitted a code review on this pull request
|
||||
- Claude has already commented on this PR (check `gh pr view <PR> --comments` for comments left by claude)
|
||||
|
||||
If any condition is true, stop and do not proceed.
|
||||
|
||||
@@ -52,14 +52,30 @@ Note: Still review Claude generated PR's.
|
||||
|
||||
6. Filter out any issues that were not validated in step 5. This step will give us our list of high signal issues for our review.
|
||||
|
||||
7. Finally, output the review.
|
||||
- If the `--comment` argument is provided, post the review as a comment on the pull request using `gh pr comment`
|
||||
- Otherwise (default), output the review directly to the terminal for local viewing
|
||||
When writing your comment, follow these guidelines:
|
||||
a. Keep your output brief
|
||||
b. Avoid emojis
|
||||
c. Link and cite relevant code, files, and URLs for each issue
|
||||
d. When citing CLAUDE.md violations, you MUST quote the exact text from CLAUDE.md that is being violated (e.g., CLAUDE.md says: "Use snake_case for variable names")
|
||||
7. If issues were found, skip to step 8 to post inline comments directly.
|
||||
|
||||
If NO issues were found, post a summary comment using `gh pr comment` (if `--comment` argument is provided):
|
||||
"No issues found. Checked for bugs and CLAUDE.md compliance."
|
||||
|
||||
8. Post inline comments for each issue using `mcp__github_inline_comment__create_inline_comment`:
|
||||
- `path`: the file path
|
||||
- `line` (and `startLine` for ranges): select the buggy lines so the user sees them
|
||||
- `body`: Brief description of the issue (no "Bug:" prefix). For small fixes (up to 5 lines changed), include a committable suggestion:
|
||||
```suggestion
|
||||
corrected code here
|
||||
```
|
||||
|
||||
**Suggestions must be COMPLETE.** If a fix requires additional changes elsewhere (e.g., renaming a variable requires updating all usages), do NOT use a suggestion block. The author should be able to click "Commit suggestion" and have a working fix - no followup work required.
|
||||
|
||||
For larger fixes (6+ lines, structural changes, or changes spanning multiple locations), do NOT use suggestion blocks. Instead:
|
||||
1. Describe what the issue is
|
||||
2. Explain the suggested fix at a high level
|
||||
3. Include a copyable prompt for Claude Code that the user can use to fix the issue, formatted as:
|
||||
```
|
||||
Fix [file:line]: [brief description of issue and suggested fix]
|
||||
```
|
||||
|
||||
**IMPORTANT: Only post ONE comment per unique issue. Do not post duplicate comments.**
|
||||
|
||||
Use this list when evaluating issues in Steps 4 and 5 (these are false positives, do NOT flag):
|
||||
|
||||
@@ -74,40 +90,18 @@ Notes:
|
||||
|
||||
- Use gh CLI to interact with GitHub (e.g., fetch pull requests, create comments). Do not use web fetch.
|
||||
- Create a todo list before starting.
|
||||
- You must cite and link each issue (e.g., if referring to a CLAUDE.md, include a link to it).
|
||||
- For your final comment, follow the following format precisely (assuming for this example that you found 3 issues):
|
||||
- You must cite and link each issue in inline comments (e.g., if referring to a CLAUDE.md, include a link to it).
|
||||
- If no issues are found, post a comment with the following format:
|
||||
|
||||
---
|
||||
|
||||
## Code review
|
||||
|
||||
Found 3 issues:
|
||||
|
||||
1. <brief description of bug> (CLAUDE.md says: "<exact quote from CLAUDE.md>")
|
||||
|
||||
<link to file and line with full sha1 + line range for context, eg. https://github.com/anthropics/claude-code/blob/1d54823877c4de72b2316a64032a54afc404e619/README.md#L13-L17>
|
||||
|
||||
2. <brief description of bug> (some/other/CLAUDE.md says: "<exact quote from CLAUDE.md>")
|
||||
|
||||
<link to file and line with full sha1 + line range for context>
|
||||
|
||||
3. <brief description of bug> (bug due to <file and code snippet>)
|
||||
|
||||
<link to file and line with full sha1 + line range for context>
|
||||
|
||||
---
|
||||
|
||||
- Or, if you found no issues:
|
||||
|
||||
---
|
||||
|
||||
## Auto code review
|
||||
|
||||
No issues found. Checked for bugs and CLAUDE.md compliance.
|
||||
|
||||
---
|
||||
|
||||
- When linking to code, follow the following format precisely, otherwise the Markdown preview won't render correctly: https://github.com/anthropics/claude-code/blob/c21d3c10bc8e898b7ac1a2d745bdc9bc4e423afe/package.json#L10-L15
|
||||
- When linking to code in inline comments, follow the following format precisely, otherwise the Markdown preview won't render correctly: https://github.com/anthropics/claude-code/blob/c21d3c10bc8e898b7ac1a2d745bdc9bc4e423afe/package.json#L10-L15
|
||||
- Requires full git sha
|
||||
- You must provide the full sha. Commands like `https://github.com/owner/repo/blob/$(git rev-parse HEAD)/foo/bar` will not work, since your comment will be directly rendered in Markdown.
|
||||
- Repo name must match the repo you're code reviewing
|
||||
|
||||
22
scripts/no-box-in-text.grit
Normal file
22
scripts/no-box-in-text.grit
Normal file
@@ -0,0 +1,22 @@
|
||||
// Biome GritQL Plugin: Prevent <Box> from being nested inside <Text>
|
||||
// This catches the Ink error: "<Box> can't be nested inside <Text> component"
|
||||
|
||||
language js;
|
||||
|
||||
// Match Text elements that contain Box children (direct nesting)
|
||||
`<Text$_>$children</Text>` where {
|
||||
$children <: contains `<Box$_>$_</Box>`,
|
||||
register_diagnostic(
|
||||
span = $children,
|
||||
message = "<Box> can't be nested inside <Text> component. Use <Box> as a sibling or wrap <Text> inside <Box> instead."
|
||||
)
|
||||
}
|
||||
|
||||
// Also match self-closing Box inside Text
|
||||
`<Text$_>$children</Text>` where {
|
||||
$children <: contains `<Box$_ />`,
|
||||
register_diagnostic(
|
||||
span = $children,
|
||||
message = "<Box> can't be nested inside <Text> component. Use <Box> as a sibling or wrap <Text> inside <Box> instead."
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user