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.
Files changed (45) hide show
  1. package/.devcontainer/CHANGELOG.md +179 -50
  2. package/.devcontainer/CLAUDE.md +24 -7
  3. package/.devcontainer/README.md +2 -0
  4. package/.devcontainer/config/main-system-prompt.md +357 -81
  5. package/.devcontainer/config/settings.json +6 -3
  6. package/.devcontainer/devcontainer.json +17 -5
  7. package/.devcontainer/features/agent-browser/README.md +65 -0
  8. package/.devcontainer/features/agent-browser/devcontainer-feature.json +23 -0
  9. package/.devcontainer/features/agent-browser/install.sh +72 -0
  10. package/.devcontainer/features/lsp-servers/devcontainer-feature.json +8 -2
  11. package/.devcontainer/features/lsp-servers/install.sh +25 -1
  12. package/.devcontainer/features/notify-hook/README.md +86 -0
  13. package/.devcontainer/features/notify-hook/devcontainer-feature.json +23 -0
  14. package/.devcontainer/features/notify-hook/install.sh +38 -0
  15. package/.devcontainer/plugins/devs-marketplace/.claude-plugin/marketplace.json +99 -0
  16. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/.claude-plugin/plugin.json +7 -0
  17. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/hooks/hooks.json +17 -0
  18. package/.devcontainer/plugins/devs-marketplace/plugins/auto-formatter/scripts/format-file.py +101 -0
  19. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/.claude-plugin/plugin.json +7 -0
  20. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/hooks/hooks.json +17 -0
  21. package/.devcontainer/plugins/devs-marketplace/plugins/auto-linter/scripts/lint-file.py +137 -0
  22. package/.devcontainer/plugins/devs-marketplace/plugins/claudepod-lsp/.claude-plugin/plugin.json +7 -0
  23. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/.claude-plugin/plugin.json +7 -0
  24. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/hooks/hooks.json +17 -0
  25. package/.devcontainer/plugins/devs-marketplace/plugins/dangerous-command-blocker/scripts/block-dangerous.py +110 -0
  26. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/.claude-plugin/plugin.json +7 -0
  27. package/.devcontainer/plugins/devs-marketplace/plugins/notify-hook/hooks/hooks.json +16 -0
  28. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/.claude-plugin/plugin.json +7 -0
  29. package/.devcontainer/plugins/devs-marketplace/plugins/planning-reminder/hooks/hooks.json +17 -0
  30. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/.claude-plugin/plugin.json +7 -0
  31. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/hooks/hooks.json +17 -0
  32. package/.devcontainer/plugins/devs-marketplace/plugins/protected-files-guard/scripts/guard-protected.py +108 -0
  33. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket:create-pr.md +337 -0
  34. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket:new.md +166 -0
  35. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket:review-commit.md +290 -0
  36. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/commands/ticket:work.md +257 -0
  37. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/plugin.json +8 -0
  38. package/.devcontainer/plugins/devs-marketplace/plugins/ticket-workflow/.claude-plugin/system-prompt.md +184 -0
  39. package/.devcontainer/scripts/setup-aliases.sh +41 -13
  40. package/.devcontainer/scripts/setup-plugins.sh +35 -13
  41. package/.devcontainer/scripts/setup.sh +1 -3
  42. package/README.md +37 -0
  43. package/package.json +1 -2
  44. package/setup.js +14 -6
  45. 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) and TypeScript (typescript-language-server)",
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 and typescript-language-server binaries
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,7 @@
1
+ {
2
+ "name": "auto-formatter",
3
+ "description": "Auto-formats files after editing (black for Python, gofmt for Go)",
4
+ "author": {
5
+ "name": "AnExiledDev"
6
+ }
7
+ }
@@ -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,7 @@
1
+ {
2
+ "name": "auto-linter",
3
+ "description": "Auto-lints files after editing (pyright for Python)",
4
+ "author": {
5
+ "name": "AnExiledDev"
6
+ }
7
+ }
@@ -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
+ }