Hooks
What devteam runs automatically, and when. A hook is a command Claude Code fires at a fixed moment, without you asking for it.
> /plugin marketplace add michael-harris/devteam > /plugin install devteam@devteam-marketplace
Ships with devteam. Installing the plugin gets these hooks.
What fires, and when
PreToolUse
- Matches
Edit|Writenode "${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.js" pre-tool-use-hook - Matches
Bashnode "${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.js" pre-tool-use-hook
PostToolUse
- Matches
Bashnode "${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.js" post-tool-use-hook - Matches
Edit|Writenode "${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.js" post-tool-use-hook
Stop
node "${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.js" stop-hook
SubagentStart
node "${CLAUDE_PLUGIN_ROOT}/hooks/log-event.js" agent_start orchestration "Subagent started"
SubagentStop
node "${CLAUDE_PLUGIN_ROOT}/hooks/log-event.js" agent_stop orchestration "Subagent completed"
TaskCompleted
node "${CLAUDE_PLUGIN_ROOT}/hooks/log-event.js" task_completed orchestration "Task marked complete"
WorktreeCreate
node "${CLAUDE_PLUGIN_ROOT}/hooks/log-event.js" worktree_created infrastructure "Git worktree created"
WorktreeRemove
node "${CLAUDE_PLUGIN_ROOT}/hooks/log-event.js" worktree_removed infrastructure "Git worktree removed"
TeammateIdle
PreCompact
node "${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.js" pre-compact
SessionStart
Fires once when a session begins, and again after a context compaction. It is where a plugin sets up its environment, or restores state the compaction dropped.
- Matches
startup|resumenode "${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.js" session-start
SessionEnd
node "${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.js" session-end
Notification
- Matches
idle_promptnode "${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.js" persistence-hook
In the plugin's words
How devteam describes its own hook set.
DevTeam declarative hooks configuration — includes command, prompt, and agent hook types
Where it lives
- hooks/install.ps1GitHub
Read the script
# DevTeam Hooks Installer (PowerShell) # Installs hooks into Claude Code configuration and git # # Usage: .\install.ps1 [-Auto] param( [switch]$Auto ) $ErrorActionPreference = "Stop" $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $ProjectRoot = Split-Path -Parent $ScriptDir Write-Host "" Write-Host "================================================================" -ForegroundColor Blue Write-Host " DevTeam Hooks Installer" -ForegroundColor Blue Write-Host "================================================================" -ForegroundColor Blue Write-Host "" # ============================================================================ # DETECT CLAUDE CODE CONFIG # ============================================================================ function Get-ClaudeConfigPath { $locations = @( "$env:USERPROFILE\.claude\settings.json", "$env:APPDATA\Claude\settings.json", "$env:LOCALAPPDATA\Claude\settings.json" ) foreach ($loc in $locations) { $dir = Split-Path $loc -Parent if (Test-Path $dir) { return $loc } } return $null } $ClaudeConfigFile = Get-ClaudeConfigPath if (-not $ClaudeConfigFile) { Write-Host "! Could not auto-detect Claude Code configuration directory." -ForegroundColor Yellow Write-Host "" Write-Host " Please manually add hooks to your Claude Code settings." Write-Host " See hooks\README.md for configuration details." Write-Host "" } else { Write-Host "ok Found Claude Code config: $ClaudeConfigFile" -ForegroundColor Green } Write-Host "" # ============================================================================ # INSTALL GIT HOOKS # ============================================================================ $gitDir = Join-Path $ProjectRoot ".git" if (Test-Path $gitDir) { Write-Host "Installing git hooks..." $gitHooksDir = Join-Path $gitDir "hooks" if (-not (Test-Path $gitHooksDir)) { New-Item -ItemType Directory -Path $gitHooksDir -Force | Out-Null } # Pre-commit hook $preCommitPath = Join-Path $gitHooksDir "pre-commit" @" #!/bin/bash exec "$ScriptDir/scope-check.sh" "@ | Set-Content $preCommitPath -Encoding UTF8 Write-Host "ok Git pre-commit hook installed" -ForegroundColor Green } else { Write-Host "! Not a git repository - skipping git hooks" -ForegroundColor Yellow } Write-Host "" # ============================================================================ # GENERATE CLAUDE CODE CONFIG # ============================================================================ Write-Host "Generating Claude Code hook configuration..." Write-Host "" $HooksConfig = @" { "hooks": { "PreToolUse": [ { "matcher": ".*", "hooks": ["powershell -ExecutionPolicy Bypass -File $ScriptDir\pre-tool-use-hook.ps1"] } ], "PostToolUse": [ { "matcher": ".*", "hooks": ["powershell -ExecutionPolicy Bypass -File $ScriptDir\post-tool-use-hook.ps1"] } ], "Stop": [ { "matcher": ".*", "hooks": ["powershell -ExecutionPolicy Bypass -File $ScriptDir\stop-hook.ps1"] } ], "PostMessage": [ { "matcher": ".*", "hooks": ["powershell -ExecutionPolicy Bypass -File $ScriptDir\persistence-hook.ps1"] } ], "SessionStart": [ { "matcher": ".*", "hooks": ["powershell -ExecutionPolicy Bypass -File $ScriptDir\session-start.ps1"] } ], "SessionEnd": [ { "matcher": ".*", "hooks": ["powershell -ExecutionPolicy Bypass -File $ScriptDir\session-end.ps1"] } ], "PreCompact": [ { "matcher": ".*", "hooks": ["powershell -ExecutionPolicy Bypass -File $ScriptDir\pre-compact.ps1"] } ] } } "@ Write-Host "Add the following to your Claude Code settings:" Write-Host "" Write-Host "----------------------------------------------------------------" -ForegroundColor Blue Write-Host $HooksConfig Write-Host "----------------------------------------------------------------" -ForegroundColor Blue Write-Host "" # ============================================================================ # AUTO-INSTALL TO SETTINGS # ============================================================================ function Install-ToSettings { param([string]$ConfigFile) if (-not (Test-Path $ConfigFile)) { # Create new config $configDir = Split-Path $ConfigFile -Parent if (-not (Test-Path $configDir)) { New-Item -ItemType Directory -Path $configDir -Force | Out-Null } $HooksConfig | Set-Content $ConfigFile -Encoding UTF8 Write-Host "ok Config file created: $ConfigFile" -ForegroundColor Green return $true } # Backup existing config Copy-Item $ConfigFile "$ConfigFile.backup" -Force Write-Host "ok Backed up existing config to $ConfigFile.backup" -ForegroundColor Green # Try to merge configs try { $existing = Get-Content $ConfigFile -Raw | ConvertFrom-Json $new = $HooksConfig | ConvertFrom-Json # Merge hooks if (-not $existing.hooks) { $existing | Add-Member -NotePropertyName "hooks" -NotePropertyValue @{} } foreach ($prop in $new.hooks.PSObject.Properties) { $existing.hooks | Add-Member -NotePropertyName $prop.Name -NotePropertyValue $prop.Value -Force } $existing | ConvertTo-Json -Depth 10 | Set-Content $ConfigFile -Encoding UTF8 Write-Host "ok Hooks merged into config" -ForegroundColor Green return $true } catch { Write-Host "! Could not merge configs: $_" -ForegroundColor Yellow Write-Host " Please manually merge the configuration." -ForegroundColor Yellow return $false } } if ($ClaudeConfigFile) { if ($Auto) { Install-ToSettings $ClaudeConfigFile | Out-Null } else { $ - hooks/install.shGitHub
Read the script
#!/bin/bash # DevTeam Hooks Installer # Installs hooks into Claude Code configuration and git # # Usage: ./install.sh [--auto] # --auto: Skip interactive prompts, auto-install everything set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # Options AUTO_MODE=false [[ "${1:-}" == "--auto" ]] && AUTO_MODE=true echo "" echo -e "${BLUE}================================================================${NC}" echo -e "${BLUE} DevTeam Hooks Installer${NC}" echo -e "${BLUE}================================================================${NC}" echo "" # ============================================================================ # DETECT CLAUDE CODE CONFIG # ============================================================================ detect_claude_config() { local config_dir="" local config_file="" # Check common locations if [[ -d "$HOME/.claude" ]]; then config_dir="$HOME/.claude" elif [[ -d "$HOME/Library/Application Support/Claude" ]]; then config_dir="$HOME/Library/Application Support/Claude" elif [[ -n "${APPDATA:-}" ]] && [[ -d "$APPDATA/Claude" ]]; then config_dir="$APPDATA/Claude" elif [[ -n "${XDG_CONFIG_HOME:-}" ]] && [[ -d "$XDG_CONFIG_HOME/claude" ]]; then config_dir="$XDG_CONFIG_HOME/claude" fi if [[ -n "$config_dir" ]]; then config_file="$config_dir/settings.json" echo "$config_file" fi } CLAUDE_CONFIG_FILE=$(detect_claude_config) if [[ -z "$CLAUDE_CONFIG_FILE" ]]; then echo -e "${YELLOW}! Could not auto-detect Claude Code configuration directory.${NC}" echo "" echo " Please manually add hooks to your Claude Code settings." echo " See hooks/README.md for configuration details." echo "" else echo -e "${GREEN}ok${NC} Found Claude Code config: $CLAUDE_CONFIG_FILE" fi echo "" # ============================================================================ # MAKE HOOKS EXECUTABLE # ============================================================================ echo "Making hooks executable..." chmod +x "$SCRIPT_DIR"/*.sh 2>/dev/null || true chmod +x "$SCRIPT_DIR"/lib/*.sh 2>/dev/null || true echo -e "${GREEN}ok${NC} Hooks are executable" echo "" # ============================================================================ # INSTALL GIT HOOKS # ============================================================================ if [[ -d "$PROJECT_ROOT/.git" ]]; then echo "Installing git hooks..." GIT_HOOKS_DIR="$PROJECT_ROOT/.git/hooks" mkdir -p "$GIT_HOOKS_DIR" # Pre-commit hook for scope checking cat > "$GIT_HOOKS_DIR/pre-commit" << EOF #!/bin/bash # DevTeam scope check hook exec "$SCRIPT_DIR/scope-check.sh" EOF chmod +x "$GIT_HOOKS_DIR/pre-commit" echo -e "${GREEN}ok${NC} Git pre-commit hook installed" else echo -e "${YELLOW}!${NC} Not a git repository - skipping git hooks" fi echo "" # ============================================================================ # GENERATE CLAUDE CODE CONFIG # ============================================================================ echo "Generating Claude Code hook configuration..." echo "" # Determine script extension based on platform if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ -n "${WINDIR:-}" ]]; then HOOK_EXT=".ps1" SHELL_CMD="powershell -ExecutionPolicy Bypass -File" else HOOK_EXT=".sh" SHELL_CMD="" fi HOOKS_CONFIG=$(cat << EOF { "hooks": { "PreToolUse": [ { "matcher": ".*", "hooks": ["$SHELL_CMD$SCRIPT_DIR/pre-tool-use-hook$HOOK_EXT"] } ], "PostToolUse": [ { "matcher": ".*", "hooks": ["$SHELL_CMD$SCRIPT_DIR/post-tool-use-hook$HOOK_EXT"] } ], "Stop": [ { "matcher": ".*", "hooks": ["$SHELL_CMD$SCRIPT_DIR/stop-hook$HOOK_EXT"] } ], "PostMessage": [ { "matcher": ".*", "hooks": ["$SHELL_CMD$SCRIPT_DIR/persistence-hook$HOOK_EXT"] } ], "SessionStart": [ { "matcher": ".*", "hooks": ["$SHELL_CMD$SCRIPT_DIR/session-start$HOOK_EXT"] } ], "SessionEnd": [ { "matcher": ".*", "hooks": ["$SHELL_CMD$SCRIPT_DIR/session-end$HOOK_EXT"] } ], "PreCompact": [ { "matcher": ".*", "hooks": ["$SHELL_CMD$SCRIPT_DIR/pre-compact$HOOK_EXT"] } ] } } EOF ) echo "Add the following to your Claude Code settings:" echo "" echo -e "${BLUE}----------------------------------------------------------------${NC}" echo "$HOOKS_CONFIG" echo -e "${BLUE}----------------------------------------------------------------${NC}" echo "" # ============================================================================ # AUTO-INSTALL TO SETTINGS # ============================================================================ install_to_settings() { local config_file="$1" if [[ ! -f "$config_file" ]]; then # Create new config mkdir -p "$(dirname "$config_file")" echo "$HOOKS_CONFIG" > "$config_file" echo -e "${GREEN}ok${NC} Config file created: $config_file" return 0 fi # Backup existing config cp "$config_file" "${config_file}.backup" echo -e "${GREEN}ok${NC} Backed up existing config to ${config_file}.backup" # Merge hooks into existing config if command -v jq &> /dev/null; then # Use jq for proper JSON merge local temp_file="${config_file}.tmp" jq -s '.[0] * .[1]' "$config_file" <(echo "$HOOKS_CONFIG") > "$temp_file" \ || { mv "${config_file}.backup" "$config_file"; echo "jq merge failed"; return 1; } mv "$temp_file" "$config_file" echo -e "${GREEN}ok${NC} Hooks merged into config" else echo -e "${YELLOW}!${NC} jq not installed. Please manually m - hooks/log-event.jsRunsGitHub
Read the script
#!/usr/bin/env node // Cross-platform event logger for DevTeam hooks // Logs events to .devteam/devteam.db via sqlite3 CLI. // Best-effort — silently no-ops if sqlite3 or DB is unavailable. 'use strict'; const { execSync } = require('child_process'); const { existsSync } = require('fs'); var eventType = process.argv[2]; var category = process.argv[3]; var message = process.argv[4]; var data = process.argv[5] || '{}'; var dbFile = '.devteam/devteam.db'; if (!eventType || !existsSync(dbFile)) { process.exit(0); } // Escape single quotes for SQL function esc(s) { return (s || '').replace(/'/g, "''"); } var sql = "INSERT INTO events (session_id, event_type, event_category, message, data, timestamp) " + "VALUES (" + "(SELECT id FROM sessions WHERE status = 'running' ORDER BY started_at DESC LIMIT 1), " + "'" + esc(eventType) + "', " + "'" + esc(category) + "', " + "'" + esc(message) + "', " + "'" + esc(data) + "', " + "datetime('now'));"; try { execSync('sqlite3 "' + dbFile + '"', { input: sql, stdio: ['pipe', 'ignore', 'ignore'], timeout: 5000, }); } catch (e) { // Best-effort logging — don't block on failure } - hooks/persistence-hook.ps1GitHub
Read the script
# DevTeam Persistence Hook (PowerShell) # Detects and prevents premature task abandonment # # Exit codes: # 0 = Allow (output is acceptable) # 2 = Block and re-engage (detected abandonment attempt) $ErrorActionPreference = "Stop" # Source common library . "$PSScriptRoot\lib\hook-common.ps1" Initialize-Hook "persistence" # ============================================================================ # CONFIGURATION # ============================================================================ $MESSAGE = $env:CLAUDE_OUTPUT $ABANDONMENT_LOG = Join-Path $script:DEVTEAM_DIR "abandonment-attempts.log" # If no message, allow if ([string]::IsNullOrEmpty($MESSAGE)) { exit 0 } # ============================================================================ # ABANDONMENT DETECTION PATTERNS # ============================================================================ # Direct abandonment phrases $GIVE_UP_PATTERNS = @( # Direct abandonment "I cannot complete this", "I'm unable to", "I can't figure out", "I don't know how to", "I'm not sure how to proceed", "I give up", "I'm stuck", "This is beyond my", "I cannot determine", "I'm at a loss", "I have no idea", # Premature completion claims "I've done what I can", "That's all I can do", "I've tried everything", "Nothing else I can try", "I'm out of ideas", "I've exhausted", "No other options", # Deflection to user "You should try", "You might want to", "You'll need to manually", "This requires human", "A human needs to", "You could try", "Perhaps you could", "Maybe you should", # False completion "I'll stop here", "Let me stop", "I think we should stop", "We can stop here", "I'm going to stop", "That should be enough", "I'll leave it here", # Excuse patterns "This is too complex", "This would take too long", "I don't have access", "I can't access", "Outside my capabilities", "Beyond my ability", "Not possible for me", "I lack the ability" ) # Passive abandonment patterns $PASSIVE_ABANDONMENT_PATTERNS = @( "Let me know if you need", "Let me know if you want", "Let me know if you'd like", "Feel free to", "You can try", "You might try", "would you like me to", "should I", "I can stop here", "we could stop", "that should work", "should be working", "I hope this helps", "Hope that helps", "Let me know if", "If you need anything else", "I'm here if you need" ) # Permission-seeking patterns $PERMISSION_SEEKING_PATTERNS = @( "Should I proceed", "Do you want me to", "Would you like me to", "Shall I", "Want me to", "Can I", "May I", "Is it okay if", "Would it be okay", "Do you mind if" ) # Legitimate completion patterns $LEGITIMATE_STOP_PATTERNS = @( "EXIT_SIGNAL: true", "EXIT_SIGNAL:true", "All tests passing", "All quality gates passed", "Task completed successfully", "Implementation complete", "Ready for review", "Committed and pushed", "All acceptance criteria met", "Successfully completed", "/devteam:end" ) # ============================================================================ # DETECTION LOGIC # ============================================================================ # Check for legitimate completion first foreach ($pattern in $LEGITIMATE_STOP_PATTERNS) { if ($MESSAGE -match [regex]::Escape($pattern)) { Write-HookInfo "persistence" "Legitimate completion detected: $pattern" exit 0 } } $DETECTED_PATTERN = $null $DETECTION_TYPE = $null # Check for direct abandonment foreach ($pattern in $GIVE_UP_PATTERNS) { if ($MESSAGE -match [regex]::Escape($pattern)) { $DETECTED_PATTERN = $pattern $DETECTION_TYPE = "direct_abandonment" break } } # Check for passive abandonment if (-not $DETECTED_PATTERN) { foreach ($pattern in $PASSIVE_ABANDONMENT_PATTERNS) { if ($MESSAGE -match [regex]::Escape($pattern)) { $DETECTED_PATTERN = $pattern $DETECTION_TYPE = "passive_abandonment" break } } } # Check for permission-seeking (only when there's an active task) if (-not $DETECTED_PATTERN) { $activeSession = Get-CurrentSession $activeTask = Get-CurrentTask if ($activeSession -and $activeTask) { foreach ($pattern in $PERMISSION_SEEKING_PATTERNS) { if ($MESSAGE -match [regex]::Escape($pattern)) { $DETECTED_PATTERN = $pattern $DETECTION_TYPE = "permission_seeking" break } } } } # If no abandonment detected, allow if (-not $DETECTED_PATTERN) { exit 0 } # ============================================================================ # ABANDONMENT RESPONSE # ============================================================================ Write-HookWarn "persistence" "Abandonment attempt detected ($DETECTION_TYPE): '$DETECTED_PATTERN'" # Get current task info $TASK_ID = Get-CurrentTask if (-not $TASK_ID) { $TASK_ID = "unknown" } # Log to abandonment file if (-not (Test-Path (Split-Path $ABANDONMENT_LOG -Parent))) { New-Item -ItemType Directory -Path (Split-Path $ABANDONMENT_LOG -Parent) -Force | Out-Null } $timestamp = Get-Date -Format "o" "[$timestamp] $DETECTION_TYPE`: '$DETECTED_PATTERN' (task: $TASK_ID)" | Add-Content $ABANDONMENT_LOG # Count abandonment attempts $ATTEMPT_COUNT = 0 if (Test-Path $ABANDONMENT_LOG) { $ATTEMPT_COUNT = (Get-Content $ABANDONMENT_LOG | Measure-Object -Line).Lines } Write-HookInfo "persistence" "Abandonment attempt #$ATTEMPT_COUNT for task: $TASK_ID" # Escape values for safe JSON embedding $SafePattern = ConvertTo-SafeJsonString $DETECTED_PATTERN $SafeType = ConvertTo-SafeJsonString $DETECTION_TYPE $SafeTaskId = ConvertTo-SafeJsonString $TASK_ID # Log to databas - hooks/persistence-hook.shGitHub
Read the script
#!/bin/bash # DevTeam Persistence Hook # Detects and prevents premature task abandonment # # This hook runs on PostMessage and analyzes Claude's output for # "give up" signals, blocking them and forcing continued effort. # # Exit codes: # 0 = Allow (output is acceptable) # 2 = Block and re-engage (detected abandonment attempt) # # Environment variables expected: # CLAUDE_OUTPUT - Claude's text output to analyze set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # Source common library with fallback paths (H6) if [[ -f "$SCRIPT_DIR/lib/hook-common.sh" ]]; then source "$SCRIPT_DIR/lib/hook-common.sh" elif [[ -f "$SCRIPT_DIR/../lib/hook-common.sh" ]]; then source "$SCRIPT_DIR/../lib/hook-common.sh" else echo "[persistence] Warning: hook-common.sh not found" >&2 exit 0 fi init_hook "persistence" # ============================================================================ # CONFIGURATION # ============================================================================ MESSAGE="${CLAUDE_OUTPUT:-}" ABANDONMENT_LOG="${DEVTEAM_DIR}/abandonment-attempts.log" # If no message, allow if [[ -z "$MESSAGE" ]]; then exit 0 fi # ============================================================================ # ABANDONMENT DETECTION PATTERNS # ============================================================================ # Direct abandonment phrases GIVE_UP_PATTERNS=( # Direct abandonment "I cannot complete this" "I'm unable to" "I can't figure out" "I don't know how to" "I'm not sure how to proceed" "I give up" "I'm stuck" "This is beyond my" "I cannot determine" "I'm at a loss" "I have no idea" # Premature completion claims "I've done what I can" "That's all I can do" "I've tried everything" "Nothing else I can try" "I'm out of ideas" "I've exhausted" "No other options" # Deflection to user "You should try" "You might want to" "You'll need to manually" "This requires human" "A human needs to" "You could try" "Perhaps you could" "Maybe you should" # False completion "I'll stop here" "Let me stop" "I think we should stop" "We can stop here" "I'm going to stop" "That should be enough" "I'll leave it here" # Excuse patterns "This is too complex" "This would take too long" "I don't have access" "I can't access" "Outside my capabilities" "Beyond my ability" "Not possible for me" "I lack the ability" ) # Passive abandonment (suggests user action instead of completing) PASSIVE_ABANDONMENT_PATTERNS=( "Let me know if you'd like" "You can try" "You might try" "would you like me to" "should I" "I can stop here" "we could stop" "that should work" "should be working" "Let me know if" "If you need anything else" "I'm here if you need" ) # Permission-seeking (asking when should be acting) PERMISSION_SEEKING_PATTERNS=( "Should I proceed" "Do you want me to" "Would you like me to" "Shall I" "Want me to" "Can I" "May I" "Is it okay if" "Would it be okay" "Do you mind if" ) # Legitimate completion patterns (allow these) LEGITIMATE_STOP_PATTERNS=( "EXIT_SIGNAL: true" "EXIT_SIGNAL:true" "All tests passing" "All quality gates passed" "Task completed successfully" "Implementation complete" "Ready for review" "Committed and pushed" "All acceptance criteria met" "Successfully completed" "/devteam:end" ) # ============================================================================ # DETECTION LOGIC # ============================================================================ # Check for legitimate completion first for pattern in "${LEGITIMATE_STOP_PATTERNS[@]}"; do if echo "$MESSAGE" | grep -qi "$pattern"; then log_info "persistence" "Legitimate completion detected: $pattern" exit 0 fi done # Track detected patterns DETECTED_PATTERN="" DETECTION_TYPE="" # Check for direct abandonment for pattern in "${GIVE_UP_PATTERNS[@]}"; do if echo "$MESSAGE" | grep -qi "$pattern"; then DETECTED_PATTERN="$pattern" DETECTION_TYPE="direct_abandonment" break fi done # Check for passive abandonment if [[ -z "$DETECTED_PATTERN" ]]; then for pattern in "${PASSIVE_ABANDONMENT_PATTERNS[@]}"; do if echo "$MESSAGE" | grep -qi "$pattern"; then DETECTED_PATTERN="$pattern" DETECTION_TYPE="passive_abandonment" break fi done fi # Check for permission-seeking (only when there's an active task) if [[ -z "$DETECTED_PATTERN" ]]; then active_session=$(get_current_session) active_task=$(get_current_task) if [[ -n "$active_session" ]] && [[ -n "$active_task" ]]; then for pattern in "${PERMISSION_SEEKING_PATTERNS[@]}"; do if echo "$MESSAGE" | grep -qi "$pattern"; then DETECTED_PATTERN="$pattern" DETECTION_TYPE="permission_seeking" break fi done fi fi # If no abandonment detected, allow if [[ -z "$DETECTED_PATTERN" ]]; then exit 0 fi # ============================================================================ # ABANDONMENT RESPONSE # ============================================================================ log_warn "persistence" "Abandonment attempt detected ($DETECTION_TYPE): '$DETECTED_PATTERN'" # Get current task info TASK_ID=$(get_current_task) [[ -z "$TASK_ID" ]] && TASK_ID="unknown" # Log to abandonment file mkdir -p "$(dirname "$ABANDONMENT_LOG")" echo "[$(date -Iseconds)] $DETECTION_TYPE: '$(printf '%s' "$DETECTED_PATTERN")' (task: $TASK_ID)" >> "$ABANDONMENT_LOG" # Count abandonment attempts for this session ATTEMPT_COUNT=$(wc -l < "$ABANDONMENT_LOG" 2>/dev/null || echo "0") log_info "persistence" "Abandonment attempt #$ATTEMPT_COUNT for task: $TASK - hooks/post-tool-use-hook.ps1GitHub
- hooks/post-tool-use-hook.shGitHub
- hooks/pre-compact.ps1GitHub
- hooks/pre-compact.shGitHub
- hooks/pre-tool-use-hook.ps1GitHub
- hooks/pre-tool-use-hook.shGitHub
- hooks/run-hook.jsRunsGitHub
Read the script
#!/usr/bin/env node // Cross-platform hook runner for DevTeam // Detects OS and runs the appropriate .sh (Unix) or .ps1 (Windows) hook script. // Always exits 0 on error — hooks must never block Claude Code. 'use strict'; const { execSync } = require('child_process'); const { existsSync } = require('fs'); const { resolve } = require('path'); const hookName = process.argv[2]; if (!hookName) { process.exit(0); } const isWindows = process.platform === 'win32'; const shScript = resolve(__dirname, hookName + '.sh'); const ps1Script = resolve(__dirname, hookName + '.ps1'); try { if (isWindows) { if (existsSync(ps1Script)) { execSync( 'powershell -ExecutionPolicy Bypass -NoProfile -File "' + ps1Script + '"', { stdio: 'inherit', timeout: 30000 } ); } } else { if (existsSync(shScript)) { execSync('bash "' + shScript + '"', { stdio: 'inherit', timeout: 30000 }); } } } catch (e) { // Hooks degrade gracefully — never block Claude Code process.exit(0); } - hooks/scope-check.ps1GitHub
- hooks/scope-check.shGitHub
- hooks/session-end.ps1GitHub
- hooks/session-end.shGitHub
- hooks/session-start.ps1GitHub
- hooks/session-start.shGitHub
- hooks/stop-hook.ps1GitHub
- hooks/stop-hook.shGitHub
All 20 scripts are listed above. The source is inlined for 6 of them, starting with whatever hooks.json actually runs. See all of them in the repo.
Read the script before you install anything that runs on your machine. This is the one part of a plugin that acts without being asked.
A Claude Code plugin providing 127 specialized AI agents with: Interview-driven planning - Clarify requirements before work begins Codebase research - Investigate patterns and blockers before implementation SQLite state management - Reliable session tracking
Repo: michael-harris/devteam

