Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion hooks/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh"
"command": "if command -v pwsh &>/dev/null || command -v powershell &>/dev/null; then powershell -ExecutionPolicy Bypass -File \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.ps1\"; else bash \"${CLAUDE_PLUGIN_ROOT}/hooks/session-start.sh\"; fi"
}
]
}
Expand Down
51 changes: 51 additions & 0 deletions hooks/session-start.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# SessionStart hook for superpowers plugin (Windows PowerShell version)

# Determine plugin root directory
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$pluginRoot = Split-Path -Parent $scriptDir

# Check if legacy skills directory exists and build warning
$warningMessage = ""
$legacySkillsDir = Join-Path $env:USERPROFILE ".config\superpowers\skills"
if (Test-Path $legacySkillsDir) {
$warningMessage = "`n`n⚠️ **WARNING:** Superpowers now uses Claude Code's skills system. Custom skills in ~/.config/superpowers/skills will not be read."
Comment on lines +9 to +11
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify the path in the warning message for Windows users.

The warning message displays ~/.config/superpowers/skills, but Windows users may not be familiar with tilde notation. The actual path being checked uses $env:USERPROFILE\.config\superpowers\skills.

Apply this diff to use a Windows-friendly path format:

 $legacySkillsDir = Join-Path $env:USERPROFILE ".config\superpowers\skills"
 if (Test-Path $legacySkillsDir) {
-    $warningMessage = "`n`n⚠️ **WARNING:** Superpowers now uses Claude Code's skills system. Custom skills in ~/.config/superpowers/skills will not be read."
+    $warningMessage = "`n`n⚠️ **WARNING:** Superpowers now uses Claude Code's skills system. Custom skills in $legacySkillsDir will not be read."
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$legacySkillsDir = Join-Path $env:USERPROFILE ".config\superpowers\skills"
if (Test-Path $legacySkillsDir) {
$warningMessage = "`n`n⚠️ **WARNING:** Superpowers now uses Claude Code's skills system. Custom skills in ~/.config/superpowers/skills will not be read."
$legacySkillsDir = Join-Path $env:USERPROFILE ".config\superpowers\skills"
if (Test-Path $legacySkillsDir) {
$warningMessage = "`n`n⚠️ **WARNING:** Superpowers now uses Claude Code's skills system. Custom skills in $legacySkillsDir will not be read."
}
🤖 Prompt for AI Agents
In hooks/session-start.ps1 around lines 9 to 11, the warning message shows the
Unix-style path (~/.config/superpowers/skills) which is unclear for Windows
users; change the message to display the actual Windows-friendly path being
checked (use $env:USERPROFILE\.config\superpowers\skills or the resolved
$legacySkillsDir) so users see the real location, and update the string
accordingly to interpolate or insert the resolved path instead of the tilde
notation.

}

# Read using-superpowers content
$skillFile = Join-Path $pluginRoot "skills\using-superpowers\SKILL.md"
if (-not (Test-Path $skillFile)) {
$output = @{
hookSpecificOutput = @{
hookEventName = "SessionStart"
additionalContext = "Error reading using-superpowers skill"
}
}
$output | ConvertTo-Json -Compress
exit 1
}

$usingSuperpowersContent = Get-Content $skillFile -Raw

# Build the additional context
$additionalContext = @"
<EXTREMELY_IMPORTANT>
You have superpowers.

**The content below is from skills/using-superpowers/SKILL.md - your introduction to using skills:**

$usingSuperpowersContent

$warningMessage
</EXTREMELY_IMPORTANT>
"@

# Output context injection as JSON
$output = @{
hookSpecificOutput = @{
hookEventName = "SessionStart"
additionalContext = $additionalContext
}
}

$output | ConvertTo-Json -Depth 10
exit 0
102 changes: 102 additions & 0 deletions lib/initialize-skills.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# Initialize or update superpowers skills repository (Windows PowerShell version)

$skillsDir = Join-Path $env:USERPROFILE ".config\superpowers\skills"
$skillsRepo = "https://github.com/obra/superpowers-skills.git"

# Check if skills directory exists and is a valid git repo
if (Test-Path (Join-Path $skillsDir ".git")) {
Set-Location $skillsDir

# Get the remote name for the current tracking branch
$trackingFull = git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>$null
$trackingRemote = ""
if ($trackingFull) {
$trackingRemote = ($trackingFull -split '/')[0]
}

# Fetch from tracking remote if set, otherwise try upstream then origin
if ($trackingRemote) {
git fetch $trackingRemote 2>$null | Out-Null
} else {
git fetch upstream 2>$null | Out-Null
if ($LASTEXITCODE -ne 0) {
git fetch origin 2>$null | Out-Null
}
}
Comment on lines +17 to +25
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Improve error handling consistency and verify origin fetch success.

The error handling patterns are inconsistent:

  • Line 19: git fetch $trackingRemote 2>$null | Out-Null
  • Lines 21, 23: git fetch upstream/origin 2>$null | Out-Null

Additionally, the fallback logic checks if upstream fetch fails (Line 22) but doesn't verify whether the origin fetch succeeds. If both fail, the script continues without confirmation that remote refs are available.

Apply this diff to verify the origin fetch and unify error suppression:

     # Fetch from tracking remote if set, otherwise try upstream then origin
     if ($trackingRemote) {
-        git fetch $trackingRemote 2>$null | Out-Null
+        git fetch $trackingRemote 2>&1 | Out-Null
     } else {
-        git fetch upstream 2>$null | Out-Null
+        git fetch upstream 2>&1 | Out-Null
         if ($LASTEXITCODE -ne 0) {
-            git fetch origin 2>$null | Out-Null
+            git fetch origin 2>&1 | Out-Null
+            if ($LASTEXITCODE -ne 0) {
+                Write-Host "Warning: Unable to fetch remote updates"
+            }
         }
     }
🤖 Prompt for AI Agents
In lib/initialize-skills.ps1 around lines 17 to 25, the git fetch error handling
is inconsistent and the fallback doesn't verify origin succeeded; change all
fetch calls to consistently suppress output with "2>$null | Out-Null" and check
$LASTEXITCODE after each fetch: if $trackingRemote is set, run "git fetch
$trackingRemote 2>$null | Out-Null" and if it fails (check $LASTEXITCODE) exit
with a clear error; if no trackingRemote, attempt "git fetch upstream 2>$null |
Out-Null" and if that fails attempt "git fetch origin 2>$null | Out-Null" and if
origin also fails exit with an error message so the script never continues
without successful remote fetch.


# Check if we can fast-forward
$local = git rev-parse '@' 2>$null
$remote = git rev-parse '@{u}' 2>$null
$base = git merge-base '@' '@{u}' 2>$null

# Try to fast-forward merge first
if ($local -and $remote -and ($local -ne $remote)) {
# Check if we can fast-forward (local is ancestor of remote)
if ($local -eq $base) {
# Fast-forward merge is possible - local is behind
Write-Host "Updating skills to latest version..."
$mergeOutput = git merge --ff-only '@{u}' 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Host "✓ Skills updated successfully"
Write-Host "SKILLS_UPDATED=true"
} else {
Write-Host "Failed to update skills"
}
} elseif ($remote -ne $base) {
# Remote has changes (local is behind or diverged)
Write-Host "SKILLS_BEHIND=true"
}
# If REMOTE = BASE, local is ahead - no action needed
}

exit 0
}

# Skills directory doesn't exist or isn't a git repo - initialize it
Write-Host "Initializing skills repository..."

# Handle migration from old installation
$oldGitDir = Join-Path $env:USERPROFILE ".config\superpowers\.git"
if (Test-Path $oldGitDir) {
Write-Host "Found existing installation. Backing up..."
Move-Item $oldGitDir "$oldGitDir.bak" -Force -ErrorAction SilentlyContinue

$oldSkillsDir = Join-Path $env:USERPROFILE ".config\superpowers\skills"
if (Test-Path $oldSkillsDir) {
Move-Item $oldSkillsDir "$oldSkillsDir.bak" -Force -ErrorAction SilentlyContinue
Write-Host "Your old skills are in $env:USERPROFILE\.config\superpowers\skills.bak"
}
Comment on lines +58 to +68
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Prevent accidental backup overwrites.

Lines 62 and 66 use Move-Item -Force, which will silently overwrite existing .bak files if the initialization script runs multiple times. This could lead to loss of previous backups.

Consider one of these approaches:

  1. Check for existing backups before moving:
 if (Test-Path $oldGitDir) {
     Write-Host "Found existing installation. Backing up..."
+    $backupPath = "$oldGitDir.bak"
+    if (Test-Path $backupPath) {
+        Write-Host "Warning: Backup already exists at $backupPath"
+        $reply = Read-Host "Overwrite existing backup? (y/N)"
+        if ($reply -notmatch '^[Yy]$') {
+            Write-Host "Skipping backup of .git directory"
+            $backupPath = $null
+        }
+    }
+    if ($backupPath) {
+        Move-Item $oldGitDir $backupPath -Force -ErrorAction SilentlyContinue
+    }
-    Move-Item $oldGitDir "$oldGitDir.bak" -Force -ErrorAction SilentlyContinue
  1. Or use timestamped backups:
-    Move-Item $oldGitDir "$oldGitDir.bak" -Force -ErrorAction SilentlyContinue
+    $timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
+    Move-Item $oldGitDir "$oldGitDir.bak.$timestamp" -Force -ErrorAction SilentlyContinue
🤖 Prompt for AI Agents
In lib/initialize-skills.ps1 around lines 58 to 68, the script uses Move-Item
-Force to rename existing .git and skills dirs to .bak which will overwrite
prior backups; change this to detect if the target .bak already exists and avoid
overwriting by either (a) if a .bak exists, append a timestamp (or incremental
suffix) to the backup name before moving, or (b) if a .bak exists, skip the move
and warn the user; remove -Force so Move-Item will not silently overwrite and
implement a small helper to generate a safe unique backup filename (e.g.,
basename + yyyyMMddHHmmss or + -1/-2) and use that when calling Move-Item.

}

# Clone the skills repository
$configDir = Join-Path $env:USERPROFILE ".config\superpowers"
if (-not (Test-Path $configDir)) {
New-Item -ItemType Directory -Path $configDir -Force | Out-Null
}

git clone $skillsRepo $skillsDir

Set-Location $skillsDir

# Check if gh CLI is installed
$ghExists = Get-Command gh -ErrorAction SilentlyContinue
if ($ghExists) {
Write-Host ""
Write-Host "GitHub CLI detected. Would you like to fork superpowers-skills?"
Write-Host "Forking allows you to share skill improvements with the community."
Write-Host ""
$reply = Read-Host "Fork superpowers-skills? (y/N)"

if ($reply -match '^[Yy]$') {
gh repo fork obra/superpowers-skills --remote=true
Write-Host "Forked! You can now contribute skills back to the community."
} else {
git remote add upstream $skillsRepo
}
} else {
# No gh, just set up upstream remote
git remote add upstream $skillsRepo
}

Write-Host "Skills repository initialized at $skillsDir"
exit 0