claudepod 1.2.2 → 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devcontainer/CHANGELOG.md +179 -50
- package/.devcontainer/CLAUDE.md +24 -7
- package/.devcontainer/README.md +2 -0
- package/.devcontainer/config/main-system-prompt.md +357 -81
- package/.devcontainer/config/settings.json +6 -3
- package/.devcontainer/devcontainer.json +17 -5
- package/.devcontainer/features/agent-browser/README.md +65 -0
- package/.devcontainer/features/agent-browser/devcontainer-feature.json +23 -0
- package/.devcontainer/features/agent-browser/install.sh +72 -0
- package/.devcontainer/features/lsp-servers/devcontainer-feature.json +8 -2
- package/.devcontainer/features/lsp-servers/install.sh +25 -1
- package/.devcontainer/features/notify-hook/README.md +86 -0
- package/.devcontainer/features/notify-hook/devcontainer-feature.json +23 -0
- package/.devcontainer/features/notify-hook/install.sh +38 -0
- package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +99 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-file.py +101 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +137 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/claudepod-lsp/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +110 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +16 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json +7 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json +17 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +108 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket:create-pr.md +337 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket:new.md +166 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket:review-commit.md +290 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket:work.md +257 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json +8 -0
- package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/system-prompt.md +184 -0
- package/.devcontainer/scripts/setup-aliases.sh +41 -13
- package/.devcontainer/scripts/setup-plugins.sh +35 -13
- package/.devcontainer/scripts/setup.sh +1 -3
- package/README.md +37 -0
- package/package.json +1 -2
- package/setup.js +14 -6
- package/.devcontainer/scripts/setup-lsp.sh +0 -20
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
VERSION="${VERSION:-latest}"
|
|
5
|
+
USERNAME="${USERNAME:-automatic}"
|
|
6
|
+
|
|
7
|
+
# Set headless as default for containers (bundled Chromium, no display needed)
|
|
8
|
+
export AGENT_BROWSER_HEADLESS=true
|
|
9
|
+
|
|
10
|
+
echo "[agent-browser] Starting installation..."
|
|
11
|
+
echo "[agent-browser] Version: ${VERSION}"
|
|
12
|
+
|
|
13
|
+
# Source nvm if available
|
|
14
|
+
if [ -f /usr/local/share/nvm/nvm.sh ]; then
|
|
15
|
+
source /usr/local/share/nvm/nvm.sh
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
# Validate npm is available
|
|
19
|
+
if ! command -v npm &>/dev/null; then
|
|
20
|
+
echo "[agent-browser] ERROR: npm not available"
|
|
21
|
+
echo " Ensure node feature is installed first"
|
|
22
|
+
exit 1
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Detect user
|
|
26
|
+
if [ "${USERNAME}" = "auto" ] || [ "${USERNAME}" = "automatic" ]; then
|
|
27
|
+
if [ -n "${_REMOTE_USER:-}" ]; then
|
|
28
|
+
USERNAME="${_REMOTE_USER}"
|
|
29
|
+
elif getent passwd vscode >/dev/null 2>&1; then
|
|
30
|
+
USERNAME="vscode"
|
|
31
|
+
elif getent passwd node >/dev/null 2>&1; then
|
|
32
|
+
USERNAME="node"
|
|
33
|
+
elif getent passwd codespace >/dev/null 2>&1; then
|
|
34
|
+
USERNAME="codespace"
|
|
35
|
+
else
|
|
36
|
+
USERNAME="root"
|
|
37
|
+
fi
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
echo "[agent-browser] Installing for user: ${USERNAME}"
|
|
41
|
+
|
|
42
|
+
# Install via npm
|
|
43
|
+
if [ "${VERSION}" = "latest" ]; then
|
|
44
|
+
NPM_PACKAGE="agent-browser"
|
|
45
|
+
else
|
|
46
|
+
NPM_PACKAGE="agent-browser@${VERSION}"
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
npm install -g "${NPM_PACKAGE}" 2>/dev/null || {
|
|
50
|
+
echo "[agent-browser] WARNING: Global install failed, trying user install"
|
|
51
|
+
su - "${USERNAME}" -c "npm install -g ${NPM_PACKAGE}" 2>/dev/null || true
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
# Download Chromium and install system dependencies
|
|
55
|
+
echo "[agent-browser] Installing Chromium and system dependencies..."
|
|
56
|
+
agent-browser install --with-deps 2>/dev/null || {
|
|
57
|
+
echo "[agent-browser] WARNING: Chromium install with deps failed, trying without --with-deps"
|
|
58
|
+
agent-browser install 2>/dev/null || true
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
echo "[agent-browser] Installed: $(agent-browser --version 2>/dev/null || echo 'unknown')"
|
|
62
|
+
echo "[agent-browser] Installation complete"
|
|
63
|
+
echo ""
|
|
64
|
+
echo "Usage:"
|
|
65
|
+
echo " agent-browser open <url> # Open a page"
|
|
66
|
+
echo " agent-browser snapshot # Get accessibility tree"
|
|
67
|
+
echo " agent-browser screenshot # Capture screenshot"
|
|
68
|
+
echo " agent-browser close # Close browser"
|
|
69
|
+
echo ""
|
|
70
|
+
echo "Host Chrome connection (if container browser insufficient):"
|
|
71
|
+
echo " # Start Chrome on host with: chrome --remote-debugging-port=9222"
|
|
72
|
+
echo " agent-browser connect 9222"
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "lsp-servers",
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"name": "LSP Servers for Claude Code",
|
|
5
|
-
"description": "Installs language server binaries for Python (pyright)
|
|
5
|
+
"description": "Installs language server binaries for Python (pyright), TypeScript (typescript-language-server), and Go (gopls)",
|
|
6
6
|
"maintainer": "AnExiledDev",
|
|
7
7
|
"documentationURL": "https://code.claude.com/docs/en/discover-plugins#code-intelligence",
|
|
8
8
|
"options": {
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
"description": "TypeScript npm package version (e.g., 'latest', '5.3.0')",
|
|
22
22
|
"default": "latest"
|
|
23
23
|
},
|
|
24
|
+
"goplsVersion": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"description": "gopls version (e.g., 'latest', 'v0.16.0')",
|
|
27
|
+
"default": "latest"
|
|
28
|
+
},
|
|
24
29
|
"username": {
|
|
25
30
|
"type": "string",
|
|
26
31
|
"description": "Container user to install for",
|
|
@@ -29,6 +34,7 @@
|
|
|
29
34
|
},
|
|
30
35
|
"installsAfter": [
|
|
31
36
|
"ghcr.io/devcontainers/features/common-utils:2",
|
|
32
|
-
"ghcr.io/devcontainers/features/node:1"
|
|
37
|
+
"ghcr.io/devcontainers/features/node:1",
|
|
38
|
+
"ghcr.io/devcontainers/features/go:1"
|
|
33
39
|
]
|
|
34
40
|
}
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
#!/bin/bash
|
|
2
2
|
# LSP Servers for Claude Code
|
|
3
|
-
# Installs pyright
|
|
3
|
+
# Installs pyright, typescript-language-server, and gopls binaries
|
|
4
4
|
|
|
5
5
|
set -euo pipefail
|
|
6
6
|
|
|
7
7
|
PYRIGHT_VERSION="${PYRIGHTVERSION:-latest}"
|
|
8
8
|
TSLSP_VERSION="${TYPESCRIPTLSPVERSION:-latest}"
|
|
9
9
|
TS_VERSION="${TYPESCRIPTVERSION:-latest}"
|
|
10
|
+
GOPLS_VERSION="${GOPLSVERSION:-latest}"
|
|
10
11
|
USERNAME="${USERNAME:-automatic}"
|
|
11
12
|
|
|
12
13
|
echo "[lsp-servers] Starting installation..."
|
|
13
14
|
echo "[lsp-servers] Pyright version: ${PYRIGHT_VERSION}"
|
|
14
15
|
echo "[lsp-servers] TypeScript LSP version: ${TSLSP_VERSION}"
|
|
15
16
|
echo "[lsp-servers] TypeScript version: ${TS_VERSION}"
|
|
17
|
+
echo "[lsp-servers] gopls version: ${GOPLS_VERSION}"
|
|
16
18
|
|
|
17
19
|
# Source nvm if available
|
|
18
20
|
if [ -f /usr/local/share/nvm/nvm.sh ]; then
|
|
@@ -73,6 +75,22 @@ install_npm_package "typescript" "typescript" "${TS_VERSION}"
|
|
|
73
75
|
# Install TypeScript Language Server
|
|
74
76
|
install_npm_package "typescript-language-server" "typescript-language-server" "${TSLSP_VERSION}"
|
|
75
77
|
|
|
78
|
+
# Install gopls (Go LSP) - uses go install since it's a Go package
|
|
79
|
+
echo "[lsp-servers] Installing gopls..."
|
|
80
|
+
if command -v go &>/dev/null; then
|
|
81
|
+
if [ "${GOPLS_VERSION}" = "latest" ]; then
|
|
82
|
+
go install "golang.org/x/tools/gopls@latest" || {
|
|
83
|
+
echo "[lsp-servers] WARNING: Failed to install gopls"
|
|
84
|
+
}
|
|
85
|
+
else
|
|
86
|
+
go install "golang.org/x/tools/gopls@${GOPLS_VERSION}" || {
|
|
87
|
+
echo "[lsp-servers] WARNING: Failed to install gopls"
|
|
88
|
+
}
|
|
89
|
+
fi
|
|
90
|
+
else
|
|
91
|
+
echo "[lsp-servers] WARNING: Go not available, skipping gopls"
|
|
92
|
+
fi
|
|
93
|
+
|
|
76
94
|
# Verify installations
|
|
77
95
|
echo ""
|
|
78
96
|
echo "[lsp-servers] Verifying installations..."
|
|
@@ -89,4 +107,10 @@ else
|
|
|
89
107
|
echo "[lsp-servers] WARNING: typescript-language-server not found in PATH"
|
|
90
108
|
fi
|
|
91
109
|
|
|
110
|
+
if command -v gopls &>/dev/null; then
|
|
111
|
+
echo "[lsp-servers] gopls: $(gopls version 2>/dev/null | head -1 || echo 'installed')"
|
|
112
|
+
else
|
|
113
|
+
echo "[lsp-servers] WARNING: gopls not found in PATH (Go may not be installed)"
|
|
114
|
+
fi
|
|
115
|
+
|
|
92
116
|
echo "[lsp-servers] Installation complete"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# notify-hook
|
|
2
|
+
|
|
3
|
+
Desktop notifications and audio chime when Claude Code finishes responding.
|
|
4
|
+
|
|
5
|
+
## How It Works
|
|
6
|
+
|
|
7
|
+
This feature uses two terminal-based notification mechanisms that work inside devcontainers:
|
|
8
|
+
|
|
9
|
+
| Mechanism | Purpose | Requirement |
|
|
10
|
+
|-----------|---------|-------------|
|
|
11
|
+
| OSC 777 escape sequence | Desktop notification popup | VSCode extension (auto-installed) |
|
|
12
|
+
| Terminal bell (`\a`) | Audio chime | VSCode setting (auto-configured) |
|
|
13
|
+
|
|
14
|
+
## Auto-Configured
|
|
15
|
+
|
|
16
|
+
All requirements are automatically configured on container rebuild:
|
|
17
|
+
|
|
18
|
+
- **Extension**: `wenbopan.vscode-terminal-osc-notifier` installed via devcontainer.json
|
|
19
|
+
- **Setting**: `terminal.integrated.enableBell: true` set via devcontainer.json
|
|
20
|
+
- **Hook**: Stop hook registered via plugin's hooks.json
|
|
21
|
+
|
|
22
|
+
No manual user setup required.
|
|
23
|
+
|
|
24
|
+
## Options
|
|
25
|
+
|
|
26
|
+
| Option | Default | Description |
|
|
27
|
+
|--------|---------|-------------|
|
|
28
|
+
| `enableBell` | `true` | Enable terminal bell (audio chime) |
|
|
29
|
+
| `enableOsc` | `true` | Enable OSC 777 desktop notifications |
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
Once the container is rebuilt, notifications trigger automatically when Claude Code finishes responding.
|
|
34
|
+
|
|
35
|
+
### Runtime Disable
|
|
36
|
+
|
|
37
|
+
Temporarily disable notifications via environment variables:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Disable audio chime
|
|
41
|
+
export NOTIFY_ENABLE_BELL=false
|
|
42
|
+
|
|
43
|
+
# Disable desktop notification
|
|
44
|
+
export NOTIFY_ENABLE_OSC=false
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Manual Trigger
|
|
48
|
+
|
|
49
|
+
Test the notification manually:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
/usr/local/bin/claude-notify
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Custom Sound
|
|
56
|
+
|
|
57
|
+
The terminal bell uses Windows "Default Beep" sound. For a custom AI-like chime:
|
|
58
|
+
|
|
59
|
+
1. Open Windows Settings → Sound → Sound Control Panel
|
|
60
|
+
2. Go to Sounds tab
|
|
61
|
+
3. Find "Default Beep" in the list
|
|
62
|
+
4. Change to a custom `.wav` file
|
|
63
|
+
|
|
64
|
+
## Troubleshooting
|
|
65
|
+
|
|
66
|
+
### No desktop notification
|
|
67
|
+
|
|
68
|
+
1. Verify extension is installed (check VSCode Extensions panel)
|
|
69
|
+
2. Check Windows notification settings allow VSCode notifications
|
|
70
|
+
3. Test manually: `printf '\033]777;notify;Test;Message\007'`
|
|
71
|
+
|
|
72
|
+
### No audio
|
|
73
|
+
|
|
74
|
+
1. Verify VSCode setting is applied: `"terminal.integrated.enableBell": true`
|
|
75
|
+
2. Check Windows sound is not muted
|
|
76
|
+
3. Test manually: `printf '\a'`
|
|
77
|
+
|
|
78
|
+
### Hook not triggering
|
|
79
|
+
|
|
80
|
+
Check the plugin's hook file exists:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cat /workspaces/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Verify Claude Code is loading the devs-marketplace plugins in settings.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "notify-hook",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"name": "Claude Code Notification Hook",
|
|
5
|
+
"description": "Desktop notifications and audio chime when Claude finishes responding",
|
|
6
|
+
"documentationURL": "https://github.com/AnExiledDev/ClaudePod",
|
|
7
|
+
"options": {
|
|
8
|
+
"enableBell": {
|
|
9
|
+
"type": "boolean",
|
|
10
|
+
"description": "Enable terminal bell (audio chime) on completion",
|
|
11
|
+
"default": true
|
|
12
|
+
},
|
|
13
|
+
"enableOsc": {
|
|
14
|
+
"type": "boolean",
|
|
15
|
+
"description": "Enable OSC 777 desktop notifications",
|
|
16
|
+
"default": true
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"installsAfter": [
|
|
20
|
+
"ghcr.io/devcontainers/features/common-utils:2",
|
|
21
|
+
"ghcr.io/anthropics/devcontainer-features/claude-code:1"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
ENABLEBELL="${ENABLEBELL:-true}"
|
|
5
|
+
ENABLEOSC="${ENABLEOSC:-true}"
|
|
6
|
+
|
|
7
|
+
echo "[notify-hook] Starting installation..."
|
|
8
|
+
echo "[notify-hook] Bell enabled: ${ENABLEBELL}"
|
|
9
|
+
echo "[notify-hook] OSC notifications enabled: ${ENABLEOSC}"
|
|
10
|
+
|
|
11
|
+
# Create the notification script
|
|
12
|
+
cat > /usr/local/bin/claude-notify << 'SCRIPT'
|
|
13
|
+
#!/bin/bash
|
|
14
|
+
# Claude Code notification script
|
|
15
|
+
# Sends desktop notification (OSC 777) and terminal bell when Claude finishes
|
|
16
|
+
|
|
17
|
+
ENABLE_BELL="${NOTIFY_ENABLE_BELL:-true}"
|
|
18
|
+
ENABLE_OSC="${NOTIFY_ENABLE_OSC:-true}"
|
|
19
|
+
PROJECT_NAME="${CLAUDE_PROJECT_NAME:-$(basename "${WORKSPACE_ROOT:-$(pwd)}")}"
|
|
20
|
+
|
|
21
|
+
if [ "${ENABLE_OSC}" = "true" ]; then
|
|
22
|
+
printf '\033]777;notify;%s;%s\007' "Claude Code" "Ready for input [${PROJECT_NAME}]"
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
if [ "${ENABLE_BELL}" = "true" ]; then
|
|
26
|
+
printf '\a'
|
|
27
|
+
fi
|
|
28
|
+
SCRIPT
|
|
29
|
+
|
|
30
|
+
chmod +x /usr/local/bin/claude-notify
|
|
31
|
+
|
|
32
|
+
# Store feature options as environment defaults
|
|
33
|
+
cat > /etc/profile.d/notify-hook.sh << EOF
|
|
34
|
+
export NOTIFY_ENABLE_BELL="${ENABLEBELL}"
|
|
35
|
+
export NOTIFY_ENABLE_OSC="${ENABLEOSC}"
|
|
36
|
+
EOF
|
|
37
|
+
|
|
38
|
+
echo "[notify-hook] Installation complete"
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://anthropic.com/claude-code/marketplace.schema.json",
|
|
3
|
+
"name": "devs-marketplace",
|
|
4
|
+
"description": "ClaudePod plugin marketplace for development tools",
|
|
5
|
+
"owner": {
|
|
6
|
+
"name": "AnExiledDev"
|
|
7
|
+
},
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "claudepod-lsp",
|
|
11
|
+
"description": "LSP servers for ClaudePod (Python, TypeScript, Go)",
|
|
12
|
+
"version": "1.0.0",
|
|
13
|
+
"source": "./plugins/claudepod-lsp",
|
|
14
|
+
"category": "development",
|
|
15
|
+
"lspServers": {
|
|
16
|
+
"pyright": {
|
|
17
|
+
"command": "pyright-langserver",
|
|
18
|
+
"args": ["--stdio"],
|
|
19
|
+
"extensionToLanguage": {
|
|
20
|
+
".py": "python",
|
|
21
|
+
".pyi": "python"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"typescript": {
|
|
25
|
+
"command": "typescript-language-server",
|
|
26
|
+
"args": ["--stdio"],
|
|
27
|
+
"extensionToLanguage": {
|
|
28
|
+
".ts": "typescript",
|
|
29
|
+
".tsx": "typescriptreact",
|
|
30
|
+
".js": "javascript",
|
|
31
|
+
".jsx": "javascriptreact",
|
|
32
|
+
".mts": "typescript",
|
|
33
|
+
".cts": "typescript",
|
|
34
|
+
".mjs": "javascript",
|
|
35
|
+
".cjs": "javascript"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"gopls": {
|
|
39
|
+
"command": "gopls",
|
|
40
|
+
"args": ["serve"],
|
|
41
|
+
"extensionToLanguage": {
|
|
42
|
+
".go": "go",
|
|
43
|
+
".mod": "go.mod",
|
|
44
|
+
".sum": "go.sum"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "ticket-workflow",
|
|
51
|
+
"description": "EARS-based ticket workflow with GitHub integration",
|
|
52
|
+
"version": "1.0.0",
|
|
53
|
+
"source": "./plugins/ticket-workflow",
|
|
54
|
+
"category": "workflow"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"name": "notify-hook",
|
|
58
|
+
"description": "Desktop notifications and audio chime when Claude finishes responding",
|
|
59
|
+
"version": "1.0.0",
|
|
60
|
+
"source": "./plugins/notify-hook",
|
|
61
|
+
"category": "productivity"
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"name": "planning-reminder",
|
|
65
|
+
"description": "Encourages plan-before-implement workflow",
|
|
66
|
+
"version": "1.0.0",
|
|
67
|
+
"source": "./plugins/planning-reminder",
|
|
68
|
+
"category": "workflow"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
"name": "dangerous-command-blocker",
|
|
72
|
+
"description": "Blocks dangerous bash commands (rm -rf, sudo rm, chmod 777, force push)",
|
|
73
|
+
"version": "1.0.0",
|
|
74
|
+
"source": "./plugins/dangerous-command-blocker",
|
|
75
|
+
"category": "safety"
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"name": "protected-files-guard",
|
|
79
|
+
"description": "Blocks modifications to .env, lock files, .git/, and credentials",
|
|
80
|
+
"version": "1.0.0",
|
|
81
|
+
"source": "./plugins/protected-files-guard",
|
|
82
|
+
"category": "safety"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"name": "auto-formatter",
|
|
86
|
+
"description": "Auto-formats files after editing (black for Python, gofmt for Go)",
|
|
87
|
+
"version": "1.0.0",
|
|
88
|
+
"source": "./plugins/auto-formatter",
|
|
89
|
+
"category": "development"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"name": "auto-linter",
|
|
93
|
+
"description": "Auto-lints files after editing (pyright for Python)",
|
|
94
|
+
"version": "1.0.0",
|
|
95
|
+
"source": "./plugins/auto-linter",
|
|
96
|
+
"category": "development"
|
|
97
|
+
}
|
|
98
|
+
]
|
|
99
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Auto-format files after editing",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"PostToolUse": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "Edit|Write",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/format-file.py",
|
|
11
|
+
"timeout": 30
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Auto-format files after editing.
|
|
4
|
+
|
|
5
|
+
Reads tool input from stdin, detects file type by extension,
|
|
6
|
+
runs appropriate formatter if available.
|
|
7
|
+
Outputs JSON with additionalContext on success.
|
|
8
|
+
Non-blocking: exit 0 regardless of formatting result.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import os
|
|
13
|
+
import subprocess
|
|
14
|
+
import sys
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
|
|
17
|
+
# Formatter configuration: extension -> (command, args, name)
|
|
18
|
+
FORMATTERS = {
|
|
19
|
+
".py": ("/usr/local/py-utils/bin/black", ["--quiet"], "Black"),
|
|
20
|
+
".pyi": ("/usr/local/py-utils/bin/black", ["--quiet"], "Black"),
|
|
21
|
+
".go": ("/usr/local/go/bin/gofmt", ["-w"], "gofmt"),
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_formatter(file_path: str) -> tuple[str, list[str], str] | None:
|
|
26
|
+
"""Get formatter config for file extension."""
|
|
27
|
+
ext = Path(file_path).suffix.lower()
|
|
28
|
+
return FORMATTERS.get(ext)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def format_file(file_path: str) -> tuple[bool, str]:
|
|
32
|
+
"""Run formatter on file.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
(success, message)
|
|
36
|
+
"""
|
|
37
|
+
formatter = get_formatter(file_path)
|
|
38
|
+
if formatter is None:
|
|
39
|
+
return True, "" # No formatter available, that's OK
|
|
40
|
+
|
|
41
|
+
cmd_path, args, name = formatter
|
|
42
|
+
|
|
43
|
+
# Check if formatter exists
|
|
44
|
+
if not os.path.exists(cmd_path):
|
|
45
|
+
return True, f"[Auto-formatter] {name} not found, skipping"
|
|
46
|
+
|
|
47
|
+
# Check if file exists
|
|
48
|
+
if not os.path.exists(file_path):
|
|
49
|
+
return True, ""
|
|
50
|
+
|
|
51
|
+
try:
|
|
52
|
+
# Run formatter
|
|
53
|
+
cmd = [cmd_path] + args + [file_path]
|
|
54
|
+
result = subprocess.run(
|
|
55
|
+
cmd,
|
|
56
|
+
capture_output=True,
|
|
57
|
+
text=True,
|
|
58
|
+
timeout=25
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if result.returncode == 0:
|
|
62
|
+
return True, f"[Auto-formatter] Formatted with {name}"
|
|
63
|
+
else:
|
|
64
|
+
# Formatting failed, but don't block
|
|
65
|
+
error = result.stderr.strip() if result.stderr else "Unknown error"
|
|
66
|
+
return True, f"[Auto-formatter] {name} warning: {error}"
|
|
67
|
+
|
|
68
|
+
except subprocess.TimeoutExpired:
|
|
69
|
+
return True, f"[Auto-formatter] {name} timed out"
|
|
70
|
+
except Exception as e:
|
|
71
|
+
return True, f"[Auto-formatter] Error: {e}"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def main():
|
|
75
|
+
try:
|
|
76
|
+
input_data = json.load(sys.stdin)
|
|
77
|
+
tool_input = input_data.get("tool_input", {})
|
|
78
|
+
file_path = tool_input.get("file_path", "")
|
|
79
|
+
|
|
80
|
+
if not file_path:
|
|
81
|
+
sys.exit(0)
|
|
82
|
+
|
|
83
|
+
_, message = format_file(file_path)
|
|
84
|
+
|
|
85
|
+
if message:
|
|
86
|
+
# Output context for Claude
|
|
87
|
+
print(json.dumps({
|
|
88
|
+
"additionalContext": message
|
|
89
|
+
}))
|
|
90
|
+
|
|
91
|
+
sys.exit(0)
|
|
92
|
+
|
|
93
|
+
except json.JSONDecodeError:
|
|
94
|
+
sys.exit(0)
|
|
95
|
+
except Exception as e:
|
|
96
|
+
print(f"Hook error: {e}", file=sys.stderr)
|
|
97
|
+
sys.exit(0)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
main()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"description": "Auto-lint files after editing",
|
|
3
|
+
"hooks": {
|
|
4
|
+
"PostToolUse": [
|
|
5
|
+
{
|
|
6
|
+
"matcher": "Edit|Write",
|
|
7
|
+
"hooks": [
|
|
8
|
+
{
|
|
9
|
+
"type": "command",
|
|
10
|
+
"command": "python3 ${CLAUDE_PLUGIN_ROOT}/scripts/lint-file.py",
|
|
11
|
+
"timeout": 60
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
17
|
+
}
|