109 lines
4.3 KiB
PowerShell
109 lines
4.3 KiB
PowerShell
# Nudge setup. Run it with Setup.bat.
|
|
# Adds a Claude Code hook that sends a phone notification through ntfy.sh
|
|
# whenever Claude Code stops to ask you something.
|
|
param(
|
|
[string]$SettingsPath = (Join-Path $env:USERPROFILE '.claude\settings.json'),
|
|
[string]$KitFolder = $PSScriptRoot,
|
|
[switch]$NoTest
|
|
)
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
function New-Topic {
|
|
$chars = 'abcdefghijklmnopqrstuvwxyz0123456789'
|
|
$bytes = New-Object byte[] 16
|
|
$rng = [System.Security.Cryptography.RandomNumberGenerator]::Create()
|
|
$rng.GetBytes($bytes)
|
|
$rng.Dispose()
|
|
$s = ''
|
|
foreach ($b in $bytes) { $s += $chars[$b % $chars.Length] }
|
|
return 'nudge-' + $s
|
|
}
|
|
|
|
$marker = 'ntfy.sh/'
|
|
$dir = Split-Path -Parent $SettingsPath
|
|
if (-not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Path $dir -Force | Out-Null }
|
|
$topicFile = Join-Path $dir 'nudge-topic.txt'
|
|
|
|
# Keep the same topic if Setup is run twice.
|
|
if (Test-Path -LiteralPath $topicFile) {
|
|
$topic = (Get-Content -LiteralPath $topicFile -Raw).Trim()
|
|
} else {
|
|
$topic = New-Topic
|
|
[System.IO.File]::WriteAllText($topicFile, $topic, (New-Object System.Text.UTF8Encoding($false)))
|
|
}
|
|
|
|
$command = 'curl.exe -s -H "Title: Claude Code" -d "Needs your attention" ' + $marker + $topic
|
|
|
|
# Read existing settings, or start fresh.
|
|
$settings = $null
|
|
if (Test-Path -LiteralPath $SettingsPath) {
|
|
$raw = Get-Content -LiteralPath $SettingsPath -Raw
|
|
if ($raw -and $raw.Trim().Length -gt 0) {
|
|
try { $settings = $raw | ConvertFrom-Json }
|
|
catch {
|
|
Write-Host 'Your settings.json could not be read, so Nudge did not touch it.'
|
|
Write-Host 'Nothing was changed. Fix or move that file and run Setup again.'
|
|
exit 1
|
|
}
|
|
Copy-Item -LiteralPath $SettingsPath -Destination ($SettingsPath + '.nudge-backup') -Force
|
|
}
|
|
}
|
|
if ($null -eq $settings) { $settings = New-Object PSObject }
|
|
|
|
if (-not ($settings.PSObject.Properties.Name -contains 'hooks')) {
|
|
$settings | Add-Member -NotePropertyName 'hooks' -NotePropertyValue (New-Object PSObject)
|
|
}
|
|
$hooks = $settings.hooks
|
|
if (-not ($hooks.PSObject.Properties.Name -contains 'Notification')) {
|
|
$hooks | Add-Member -NotePropertyName 'Notification' -NotePropertyValue @()
|
|
}
|
|
|
|
# Drop any earlier Nudge entry, keep everything else.
|
|
$keep = @()
|
|
foreach ($entry in @($hooks.Notification)) {
|
|
$isNudge = $false
|
|
foreach ($h in @($entry.hooks)) {
|
|
if ($h.command -and $h.command.ToString().Contains($marker + 'nudge-')) { $isNudge = $true }
|
|
}
|
|
if (-not $isNudge) { $keep += $entry }
|
|
}
|
|
$new = [PSCustomObject]@{
|
|
matcher = ''
|
|
hooks = @([PSCustomObject]@{ type = 'command'; command = $command })
|
|
}
|
|
$hooks.Notification = @($keep + $new)
|
|
|
|
$json = $settings | ConvertTo-Json -Depth 20
|
|
[System.IO.File]::WriteAllText($SettingsPath, $json, (New-Object System.Text.UTF8Encoding($false)))
|
|
|
|
# Make a ready-to-use chat skill with the topic filled in.
|
|
$template = Join-Path $KitFolder 'chat-skill\SKILL.md'
|
|
if (Test-Path -LiteralPath $template) {
|
|
$out = Join-Path $KitFolder 'MY-SKILL.md'
|
|
$text = (Get-Content -LiteralPath $template -Raw).Replace('YOUR-TOPIC', $topic)
|
|
[System.IO.File]::WriteAllText($out, $text, (New-Object System.Text.UTF8Encoding($false)))
|
|
}
|
|
|
|
Write-Host ''
|
|
Write-Host 'Nudge is set up.'
|
|
Write-Host ''
|
|
Write-Host 'Your private topic name is:'
|
|
Write-Host (' ' + $topic)
|
|
Write-Host 'Keep it to yourself. Anyone who knows it can send messages to your phone.'
|
|
Write-Host ''
|
|
Write-Host 'Next, on your phone:'
|
|
Write-Host ' 1. Install the ntfy app.'
|
|
Write-Host ' 2. Tap +, type the topic name above, leave "use another server" unchecked,'
|
|
Write-Host ' and tick "instant delivery in doze mode". Tap Subscribe.'
|
|
Write-Host ' 3. Allow notifications, and set the ntfy battery option to Unrestricted.'
|
|
Write-Host ''
|
|
if (-not $NoTest) {
|
|
$a = Read-Host 'Subscribed? Press Enter to send a test message (or type N to skip)'
|
|
if ($a -notmatch '^[Nn]') {
|
|
& curl.exe -s -H 'Title: Nudge' -d 'It works. This is a test from Nudge.' ($marker + $topic) | Out-Null
|
|
Write-Host 'Test sent. It should show up on your phone in a moment.'
|
|
}
|
|
}
|
|
Write-Host ''
|
|
Write-Host 'Restart Claude Code so it loads the hook.'
|