mirror of
https://github.com/anthropics/claude-code.git
synced 2026-04-18 17:32:47 +00:00
Compare commits
7 Commits
claude/sla
...
claude/sla
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c57e786c15 | ||
|
|
eb87245010 | ||
|
|
3680637065 | ||
|
|
2192c86c20 | ||
|
|
dfd3494132 | ||
|
|
e8cca9a7af | ||
|
|
6358669884 |
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,5 +1,32 @@
|
||||
# Changelog
|
||||
|
||||
## 2.0.69
|
||||
|
||||
- Minor bugfixes
|
||||
|
||||
## 2.0.68
|
||||
|
||||
- Fixed IME (Input Method Editor) support for languages like Chinese, Japanese, and Korean by correctly positioning the composition window at the cursor
|
||||
- Fixed a bug where disallowed MCP tools were visible to the model
|
||||
- Fixed an issue where steering messages could be lost while a subagent is working
|
||||
- Fixed Option+Arrow word navigation treating entire CJK (Chinese, Japanese, Korean) text sequences as a single word instead of navigating by word boundaries
|
||||
- Improved plan mode exit UX: show simplified yes/no dialog when exiting with empty or missing plan instead of throwing an error
|
||||
- Add support for enterprise managed settings. Contact your Anthropic account team to enable this feature.
|
||||
|
||||
## 2.0.67
|
||||
|
||||
- Thinking mode is now enabled by default for Opus 4.5
|
||||
- Thinking mode configuration has moved to /config
|
||||
- Added search functionality to `/permissions` command with `/` keyboard shortcut for filtering rules by tool name
|
||||
- Show reason why autoupdater is disabled in `/doctor`
|
||||
- Fixed false "Another process is currently updating Claude" error when running `claude update` while another instance is already on the latest version
|
||||
- Fixed MCP servers from `.mcp.json` being stuck in pending state when running in non-interactive mode (`-p` flag or piped input)
|
||||
- Fixed scroll position resetting after deleting a permission rule in `/permissions`
|
||||
- Fixed word deletion (opt+delete) and word navigation (opt+arrow) not working correctly with non-Latin text such as Cyrillic, Greek, Arabic, Hebrew, Thai, and Chinese
|
||||
- Fixed `claude install --force` not bypassing stale lock files
|
||||
- Fixed consecutive @~/ file references in CLAUDE.md being incorrectly parsed due to markdown strikethrough interference
|
||||
- Windows: Fixed plugin MCP servers failing due to colons in log directory paths
|
||||
|
||||
## 2.0.65
|
||||
|
||||
- Added ability to switch models while writing a prompt using alt+p (linux, windows), option+p (macos).
|
||||
@@ -23,6 +50,7 @@
|
||||
- VSCode: Added copy-to-clipboard button on code blocks and bash tool inputs
|
||||
- VSCode: Fixed extension not working on Windows ARM64 by falling back to x64 binary via emulation
|
||||
- Bedrock: Improve efficiency of token counting
|
||||
- Bedrock: Add support for `aws login` AWS Management Console credentials
|
||||
- Unshipped AgentOutputTool and BashOutputTool, in favor of a new unified TaskOutputTool
|
||||
|
||||
## 2.0.62
|
||||
|
||||
@@ -93,11 +93,6 @@ Found 3 issues:
|
||||
|
||||
<link to file and line with full sha1 + line range for context>
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||
|
||||
|
||||
<sub>- If this code review was useful, please react with 👍. Otherwise, react with 👎.</sub>
|
||||
|
||||
---
|
||||
|
||||
- Or, if you found no issues:
|
||||
@@ -108,8 +103,6 @@ Found 3 issues:
|
||||
|
||||
No issues found. Checked for bugs and CLAUDE.md compliance.
|
||||
|
||||
🤖 Generated with [Claude Code](https://claude.ai/code)
|
||||
|
||||
---
|
||||
|
||||
- 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
|
||||
|
||||
@@ -44,6 +44,17 @@
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"Notification": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/notification.py",
|
||||
"timeout": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
143
plugins/hookify/hooks/notification.py
Normal file
143
plugins/hookify/hooks/notification.py
Normal file
@@ -0,0 +1,143 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Notification hook executor for hookify plugin.
|
||||
|
||||
This script is called by Claude Code when notifications are sent.
|
||||
It formats teammate idle notifications and other IPC messages for display.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# CRITICAL: Add plugin root to Python path for imports
|
||||
PLUGIN_ROOT = os.environ.get('CLAUDE_PLUGIN_ROOT')
|
||||
if PLUGIN_ROOT:
|
||||
parent_dir = os.path.dirname(PLUGIN_ROOT)
|
||||
if parent_dir not in sys.path:
|
||||
sys.path.insert(0, parent_dir)
|
||||
if PLUGIN_ROOT not in sys.path:
|
||||
sys.path.insert(0, PLUGIN_ROOT)
|
||||
|
||||
|
||||
def format_idle_notification(data: dict) -> str:
|
||||
"""Format an idle notification for display.
|
||||
|
||||
Args:
|
||||
data: The notification data containing type, from, timestamp, etc.
|
||||
|
||||
Returns:
|
||||
Formatted string for display
|
||||
"""
|
||||
worker_name = data.get('from', 'worker')
|
||||
timestamp = data.get('timestamp', '')
|
||||
|
||||
# Format timestamp if present
|
||||
time_str = ''
|
||||
if timestamp:
|
||||
try:
|
||||
dt = datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
|
||||
time_str = dt.strftime('%H:%M:%S')
|
||||
except (ValueError, AttributeError):
|
||||
time_str = ''
|
||||
|
||||
# Build the formatted output using the suggested format
|
||||
lines = [f"⏺ {worker_name}"]
|
||||
if time_str:
|
||||
lines.append(f" ⎿ Status is idle ({time_str})")
|
||||
else:
|
||||
lines.append(" ⎿ Status is idle")
|
||||
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def format_notification(notification_content: str) -> dict:
|
||||
"""Parse and format a notification message.
|
||||
|
||||
Args:
|
||||
notification_content: Raw notification content (may be JSON or plain text)
|
||||
|
||||
Returns:
|
||||
Dict with formatted systemMessage
|
||||
"""
|
||||
# Try to parse as JSON first
|
||||
try:
|
||||
data = json.loads(notification_content)
|
||||
|
||||
# Check if this is an idle notification
|
||||
if isinstance(data, dict) and data.get('type') == 'idle_notification':
|
||||
formatted = format_idle_notification(data)
|
||||
return {"systemMessage": formatted}
|
||||
|
||||
# Handle other notification types
|
||||
notification_type = data.get('type', '') if isinstance(data, dict) else ''
|
||||
|
||||
if notification_type == 'status_update':
|
||||
worker = data.get('from', 'worker')
|
||||
status = data.get('status', 'unknown')
|
||||
return {"systemMessage": f"⏺ {worker}\n ⎿ Status: {status}"}
|
||||
|
||||
if notification_type == 'progress_update':
|
||||
worker = data.get('from', 'worker')
|
||||
progress = data.get('progress', '')
|
||||
return {"systemMessage": f"⏺ {worker}\n ⎿ {progress}"}
|
||||
|
||||
# For unknown JSON types, still try to format nicely
|
||||
if isinstance(data, dict) and 'from' in data:
|
||||
worker = data.get('from', 'worker')
|
||||
msg = data.get('message', data.get('status', 'update'))
|
||||
return {"systemMessage": f"⏺ {worker}\n ⎿ {msg}"}
|
||||
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
# Not JSON, return as-is
|
||||
pass
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def main():
|
||||
"""Main entry point for Notification hook."""
|
||||
try:
|
||||
# Read input from stdin
|
||||
input_data = json.load(sys.stdin)
|
||||
|
||||
# Get notification content
|
||||
notification = input_data.get('notification', '')
|
||||
|
||||
# Also check for raw notification data in the input
|
||||
if not notification and input_data.get('type') == 'idle_notification':
|
||||
# The input itself is an idle notification
|
||||
formatted = format_idle_notification(input_data)
|
||||
result = {"systemMessage": formatted}
|
||||
elif notification:
|
||||
# Format the notification content
|
||||
result = format_notification(notification)
|
||||
else:
|
||||
# Check if the input looks like an IPC message
|
||||
if input_data.get('type') in ['idle_notification', 'status_update', 'progress_update']:
|
||||
if input_data.get('type') == 'idle_notification':
|
||||
formatted = format_idle_notification(input_data)
|
||||
result = {"systemMessage": formatted}
|
||||
else:
|
||||
worker = input_data.get('from', 'worker')
|
||||
status = input_data.get('status', input_data.get('type', 'update'))
|
||||
result = {"systemMessage": f"⏺ {worker}\n ⎿ {status}"}
|
||||
else:
|
||||
result = {}
|
||||
|
||||
# Always output JSON
|
||||
print(json.dumps(result), file=sys.stdout)
|
||||
|
||||
except Exception as e:
|
||||
error_output = {
|
||||
"systemMessage": f"Notification format error: {str(e)}"
|
||||
}
|
||||
print(json.dumps(error_output), file=sys.stdout)
|
||||
|
||||
finally:
|
||||
# ALWAYS exit 0
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -42,7 +42,6 @@ assistant: "[How assistant should respond and use this agent]"
|
||||
model: inherit
|
||||
color: blue
|
||||
tools: ["Read", "Write", "Grep"]
|
||||
skills: skill-name-1, skill-name-2
|
||||
---
|
||||
|
||||
You are [agent role description]...
|
||||
@@ -160,35 +159,6 @@ tools: ["Read", "Write", "Grep", "Bash"]
|
||||
- Testing: `["Read", "Bash", "Grep"]`
|
||||
- Full access: Omit field or use `["*"]`
|
||||
|
||||
### skills (optional)
|
||||
|
||||
Declare skills to auto-load when this agent is invoked.
|
||||
|
||||
**Format:** Comma-separated list of skill names
|
||||
|
||||
```yaml
|
||||
skills: database-migration, postgres-ops
|
||||
```
|
||||
|
||||
**Purpose:** Ensures specific skills are always available to the agent, regardless of context matching. This is especially useful for:
|
||||
- Skills that should persist across context compaction
|
||||
- Specialized agents that always need certain domain knowledge
|
||||
- Ensuring consistent agent behavior with required skills
|
||||
|
||||
**Example:**
|
||||
```yaml
|
||||
---
|
||||
name: db-admin
|
||||
description: Use this agent for database administration tasks...
|
||||
model: inherit
|
||||
color: cyan
|
||||
tools: ["Bash", "Read", "Write"]
|
||||
skills: database-migration, sql-optimization
|
||||
---
|
||||
```
|
||||
|
||||
**Best practice:** Only declare skills essential to the agent's core function. Let context-triggered skills load naturally for optional capabilities.
|
||||
|
||||
## System Prompt Design
|
||||
|
||||
The markdown body becomes the agent's system prompt. Write in second person, addressing the agent directly.
|
||||
@@ -385,7 +355,6 @@ Output: [What to provide]
|
||||
| model | Yes | inherit/sonnet/opus/haiku | inherit |
|
||||
| color | Yes | Color name | blue |
|
||||
| tools | No | Array of tool names | ["Read", "Grep"] |
|
||||
| skills | No | Comma-separated names | skill-a, skill-b |
|
||||
|
||||
### Best Practices
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ description: Brief description
|
||||
allowed-tools: Read, Write
|
||||
model: sonnet
|
||||
argument-hint: [arg1] [arg2]
|
||||
skills: skill-a, skill-b
|
||||
---
|
||||
|
||||
Command prompt content here...
|
||||
@@ -322,51 +321,6 @@ disable-model-invocation: true
|
||||
- Document why in command comments
|
||||
- Consider if command should exist if always manual
|
||||
|
||||
### skills
|
||||
|
||||
**Type:** String (comma-separated list)
|
||||
**Required:** No
|
||||
**Default:** None
|
||||
|
||||
**Purpose:** Declare skills to auto-load when this command executes. Ensures specific skills are always available regardless of context matching.
|
||||
|
||||
**Examples:**
|
||||
```yaml
|
||||
skills: database-migration, sql-optimization
|
||||
```
|
||||
|
||||
**When to use:**
|
||||
|
||||
1. **Commands requiring domain knowledge:** Ensure relevant skills load
|
||||
```yaml
|
||||
---
|
||||
description: Run database migration
|
||||
skills: database-migration, postgres-ops
|
||||
---
|
||||
```
|
||||
|
||||
2. **Skills that should persist across compaction:** Force-load skills that might otherwise be lost
|
||||
```yaml
|
||||
---
|
||||
description: Complex multi-step workflow
|
||||
skills: workflow-skill, validation-skill
|
||||
---
|
||||
```
|
||||
|
||||
3. **Specialized commands:** Commands that always need specific expertise
|
||||
```yaml
|
||||
---
|
||||
description: Generate API documentation
|
||||
skills: api-documentation, openapi-spec
|
||||
---
|
||||
```
|
||||
|
||||
**Best practices:**
|
||||
- Only declare skills essential to the command's function
|
||||
- Use skill names exactly as defined in SKILL.md frontmatter
|
||||
- Keep the list focused (2-4 skills max)
|
||||
- Let context-triggered skills load naturally for optional capabilities
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Minimal Command
|
||||
@@ -497,7 +451,6 @@ Before committing command:
|
||||
- [ ] model is valid value if specified
|
||||
- [ ] argument-hint matches positional arguments
|
||||
- [ ] disable-model-invocation used appropriately
|
||||
- [ ] skills references valid skill names if specified
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
@@ -507,5 +460,4 @@ Before committing command:
|
||||
4. **Choose right model:** Use haiku for speed, opus for complexity
|
||||
5. **Manual-only sparingly:** Only use disable-model-invocation when necessary
|
||||
6. **Clear descriptions:** Make commands discoverable in `/help`
|
||||
7. **Declare essential skills:** Use skills field for domain knowledge that must persist
|
||||
8. **Test thoroughly:** Verify frontmatter works as expected
|
||||
7. **Test thoroughly:** Verify frontmatter works as expected
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#!/bin/bash
|
||||
# Example: Format teammate idle notification
|
||||
#
|
||||
# This script demonstrates how to format raw JSON idle notifications
|
||||
# into user-friendly display format.
|
||||
#
|
||||
# Usage: echo '{"type":"idle_notification","from":"worker-1","timestamp":"..."}' | ./format-idle-notification.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Read JSON from stdin
|
||||
input=$(cat)
|
||||
|
||||
# Parse notification type
|
||||
notification_type=$(echo "$input" | jq -r '.type // empty' 2>/dev/null || echo "")
|
||||
|
||||
if [[ "$notification_type" == "idle_notification" ]]; then
|
||||
# Extract fields
|
||||
worker_name=$(echo "$input" | jq -r '.from // "worker"')
|
||||
timestamp=$(echo "$input" | jq -r '.timestamp // empty')
|
||||
|
||||
# Format timestamp if present
|
||||
time_str=""
|
||||
if [[ -n "$timestamp" ]]; then
|
||||
# Try to format the timestamp
|
||||
time_str=$(date -d "$timestamp" '+%H:%M:%S' 2>/dev/null || echo "")
|
||||
fi
|
||||
|
||||
# Output formatted notification using recommended format:
|
||||
# ⏺ worker-1
|
||||
# ⎿ Status is idle
|
||||
echo "⏺ $worker_name"
|
||||
if [[ -n "$time_str" ]]; then
|
||||
echo " ⎿ Status is idle ($time_str)"
|
||||
else
|
||||
echo " ⎿ Status is idle"
|
||||
fi
|
||||
|
||||
# Output JSON for hook system
|
||||
if [[ -n "$time_str" ]]; then
|
||||
jq -n --arg msg "⏺ $worker_name\n ⎿ Status is idle ($time_str)" \
|
||||
'{"systemMessage": $msg}'
|
||||
else
|
||||
jq -n --arg msg "⏺ $worker_name\n ⎿ Status is idle" \
|
||||
'{"systemMessage": $msg}'
|
||||
fi
|
||||
else
|
||||
# Not an idle notification, pass through
|
||||
echo "$input"
|
||||
fi
|
||||
@@ -344,3 +344,69 @@ fi
|
||||
- Per-project settings
|
||||
- Team-specific rules
|
||||
- Dynamic validation criteria
|
||||
|
||||
## Pattern 11: Format Teammate Idle Notifications
|
||||
|
||||
Format raw JSON IPC messages from workers/teammates into user-friendly display:
|
||||
|
||||
```json
|
||||
{
|
||||
"Notification": [
|
||||
{
|
||||
"matcher": "*",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/hooks/notification.py",
|
||||
"timeout": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Example script (format-idle-notification.py):**
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
import json
|
||||
|
||||
def format_idle_notification(data):
|
||||
"""Format idle notification for display."""
|
||||
worker_name = data.get('from', 'worker')
|
||||
# Output format:
|
||||
# ⏺ worker-1
|
||||
# ⎿ Status is idle
|
||||
return f"⏺ {worker_name}\n ⎿ Status is idle"
|
||||
|
||||
def main():
|
||||
input_data = json.load(sys.stdin)
|
||||
|
||||
# Check for idle notification
|
||||
if input_data.get('type') == 'idle_notification':
|
||||
formatted = format_idle_notification(input_data)
|
||||
print(json.dumps({"systemMessage": formatted}))
|
||||
else:
|
||||
print(json.dumps({}))
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
```
|
||||
|
||||
**Input (raw JSON IPC message):**
|
||||
```json
|
||||
{"type": "idle_notification", "from": "worker-1", "timestamp": "2025-12-15T05:22:40.320Z"}
|
||||
```
|
||||
|
||||
**Output (formatted for display):**
|
||||
```
|
||||
⏺ worker-1
|
||||
⎿ Status is idle
|
||||
```
|
||||
|
||||
**Use for:**
|
||||
- Formatting teammate/worker status messages
|
||||
- Converting internal IPC messages to user-friendly display
|
||||
- Multi-agent swarm coordination UI
|
||||
- Any notification that shouldn't show raw JSON to users
|
||||
|
||||
Reference in New Issue
Block a user