feat(doctor): add --summary/-Summary output mode
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c94dfdcb30
commit
3fab891383
4 files changed
+51
-10
No files matched your search
+15
-5
@@ -1,11 +1,16 @@
|
|||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param()
|
param([switch]$Summary)
|
||||||
$ErrorActionPreference = 'Stop'
|
$ErrorActionPreference = 'Stop'
|
||||||
$root = Split-Path $PSScriptRoot -Parent
|
$root = Split-Path $PSScriptRoot -Parent
|
||||||
. (Join-Path $PSScriptRoot 'lib/manifest.ps1')
|
. (Join-Path $PSScriptRoot 'lib/manifest.ps1')
|
||||||
$homePath = [Environment]::GetFolderPath('UserProfile')
|
$homePath = [Environment]::GetFolderPath('UserProfile')
|
||||||
$fail = $false
|
$script:fail = $false
|
||||||
function Result($state, $message) { Write-Output ("$state $message"); if ($state -eq 'FAIL') { $script:fail = $true } }
|
$script:checkCount = 0
|
||||||
|
function Result($state, $message) {
|
||||||
|
$script:checkCount++
|
||||||
|
if ($state -eq 'FAIL') { $script:fail = $true }
|
||||||
|
if (-not $Summary -or $script:fail -or $state -ne 'PASS') { Write-Output ("$state $message") }
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($tool in @('codex','claude')) { if (Get-Command $tool -ErrorAction SilentlyContinue) { $version = & $tool --version 2>&1 | Select-Object -First 1; Result 'PASS' "$tool available ($version)" } else { Result 'WARN' "$tool unavailable" } }
|
foreach ($tool in @('codex','claude')) { if (Get-Command $tool -ErrorAction SilentlyContinue) { $version = & $tool --version 2>&1 | Select-Object -First 1; Result 'PASS' "$tool available ($version)" } else { Result 'WARN' "$tool unavailable" } }
|
||||||
if ($PSVersionTable.PSVersion.Major -ge 6 -and -not $IsWindows) { if (Get-Command jq -ErrorAction SilentlyContinue) { Result 'PASS' 'jq available for Claude status line' } else { Result 'WARN' 'jq unavailable; Claude status line is disabled' } }
|
if ($PSVersionTable.PSVersion.Major -ge 6 -and -not $IsWindows) { if (Get-Command jq -ErrorAction SilentlyContinue) { Result 'PASS' 'jq available for Claude status line' } else { Result 'WARN' 'jq unavailable; Claude status line is disabled' } }
|
||||||
@@ -42,7 +47,7 @@ if (Test-Path -LiteralPath $settingsPath) {
|
|||||||
}
|
}
|
||||||
$configTomlPath = Join-Path $homePath '.codex/config.toml'
|
$configTomlPath = Join-Path $homePath '.codex/config.toml'
|
||||||
if (Test-Path -LiteralPath $configTomlPath) {
|
if (Test-Path -LiteralPath $configTomlPath) {
|
||||||
$parseResult = & python -c 'import sys,tomllib,pathlib; tomllib.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8-sig"))' $configTomlPath 2>&1
|
try { $parseResult = & python -c 'import sys,tomllib,pathlib; tomllib.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8-sig"))' $configTomlPath 2>&1 } catch { $parseResult = $_ }
|
||||||
if ($LASTEXITCODE -eq 0) { Result 'PASS' 'installed .codex/config.toml parses as TOML' } else { Result 'FAIL' "installed .codex/config.toml is invalid TOML: $parseResult" }
|
if ($LASTEXITCODE -eq 0) { Result 'PASS' 'installed .codex/config.toml parses as TOML' } else { Result 'FAIL' "installed .codex/config.toml is invalid TOML: $parseResult" }
|
||||||
if (Get-Command codex -ErrorAction SilentlyContinue) {
|
if (Get-Command codex -ErrorAction SilentlyContinue) {
|
||||||
$previousCodexHome = $env:CODEX_HOME
|
$previousCodexHome = $env:CODEX_HOME
|
||||||
@@ -77,6 +82,11 @@ foreach ($client in @('codex','claude')) {
|
|||||||
if ($registered) { Result 'PASS' "$client Stop hook is registered" } else { Result 'WARN' "$client ships hook utilities, but no Stop hook is registered" }
|
if ($registered) { Result 'PASS' "$client Stop hook is registered" } else { Result 'WARN' "$client ships hook utilities, but no Stop hook is registered" }
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($fail) { exit 1 }
|
if ($Summary) {
|
||||||
|
if ($script:fail) { Write-Output "Doctor: FAIL | $script:checkCount checks"; exit 1 }
|
||||||
|
Write-Output "Doctor: PASS | $script:checkCount checks"
|
||||||
|
} else {
|
||||||
|
if ($script:fail) { exit 1 }
|
||||||
Write-Output 'PASS doctor'
|
Write-Output 'PASS doctor'
|
||||||
|
}
|
||||||
exit 0
|
exit 0
|
||||||
+17
-2
@@ -1,14 +1,24 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
|
summary=false
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--summary) summary=true; shift ;;
|
||||||
|
*) printf 'Unknown argument: %s\n' "$1" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
||||||
. "$root/scripts/lib/manifest.sh"
|
. "$root/scripts/lib/manifest.sh"
|
||||||
home_path=${HOME:?HOME is required}
|
home_path=${HOME:?HOME is required}
|
||||||
failed=false
|
failed=false
|
||||||
|
check_count=0
|
||||||
|
|
||||||
result() {
|
result() {
|
||||||
printf '%s %s\n' "$1" "$2"
|
check_count=$((check_count + 1))
|
||||||
[ "$1" != FAIL ] || failed=true
|
[ "$1" != FAIL ] || failed=true
|
||||||
|
if [ "$summary" = false ] || [ "$failed" = true ] || [ "$1" != PASS ]; then printf '%s %s\n' "$1" "$2"; fi
|
||||||
}
|
}
|
||||||
|
|
||||||
for tool in codex claude; do
|
for tool in codex claude; do
|
||||||
@@ -85,5 +95,10 @@ for path in shared/rules shared/skills shared/agents shared/hooks/scripts; do
|
|||||||
[ -e "$root/$path" ] && result PASS "source $path" || result FAIL "missing $path"
|
[ -e "$root/$path" ] && result PASS "source $path" || result FAIL "missing $path"
|
||||||
done
|
done
|
||||||
|
|
||||||
"$failed" && exit 1
|
if [ "$summary" = true ]; then
|
||||||
|
if [ "$failed" = true ]; then printf 'Doctor: FAIL | %s checks\n' "$check_count"; exit 1; fi
|
||||||
|
printf 'Doctor: PASS | %s checks\n' "$check_count"
|
||||||
|
else
|
||||||
|
[ "$failed" = false ] || exit 1
|
||||||
printf 'PASS doctor\n'
|
printf 'PASS doctor\n'
|
||||||
|
fi
|
||||||
@@ -5,8 +5,10 @@ $bashHook = Get-Content -LiteralPath (Join-Path $root 'shared/hooks/flashbang.sh
|
|||||||
if ($powerShellHook -notmatch '\$HoldMs\s*=\s*250' -or $powerShellHook -notmatch '\$FadeMs\s*=\s*250' -or ([regex]::Matches($bashHook, 'default=250').Count -lt 2)) { throw 'Flashbang defaults must total 500 milliseconds on Windows and Linux.' }
|
if ($powerShellHook -notmatch '\$HoldMs\s*=\s*250' -or $powerShellHook -notmatch '\$FadeMs\s*=\s*250' -or ([regex]::Matches($bashHook, 'default=250').Count -lt 2)) { throw 'Flashbang defaults must total 500 milliseconds on Windows and Linux.' }
|
||||||
$statusInput = '{"model":{"display_name":"Test Model"},"effort":{"level":"high"},"workspace":{"current_dir":"' + $root.Replace('\','\\') + '","repo":{"name":"TestRepo"}},"context_window":{"context_window_size":200000,"used_percentage":8.5,"total_input_tokens":15500,"total_output_tokens":1200}}'
|
$statusInput = '{"model":{"display_name":"Test Model"},"effort":{"level":"high"},"workspace":{"current_dir":"' + $root.Replace('\','\\') + '","repo":{"name":"TestRepo"}},"context_window":{"context_window_size":200000,"used_percentage":8.5,"total_input_tokens":15500,"total_output_tokens":1200}}'
|
||||||
$branch = & git -C $root branch --show-current
|
$branch = & git -C $root branch --show-current
|
||||||
$statusOutput = $statusInput | & (Join-Path $root 'shared/statusline/statusline.ps1')
|
$statusOutput = ($statusInput | & (Join-Path $root 'shared/statusline/statusline.ps1')) -join "`n"
|
||||||
if ($statusOutput -ne "Model: Test Model | Effort: high | Repo: TestRepo | Branch: $branch | Max Context: 200000 | Used Context: 9% | Used Tokens: 16700") { throw 'PowerShell status line output is incorrect.' }
|
$plainStatusOutput = [regex]::Replace($statusOutput, [char]27 + '\[[0-9;]*m', '')
|
||||||
|
$expectedStatusOutput = "Test Model $([char]0x00B7) high`nTestRepo on $branch $([char]0x00B7) 9% ctx (16.7k tok)"
|
||||||
|
if ($plainStatusOutput -ne $expectedStatusOutput) { throw 'PowerShell status line output is incorrect.' }
|
||||||
$sourceAgentCount = @(Get-ChildItem (Join-Path $root 'shared/agents') -Directory).Count
|
$sourceAgentCount = @(Get-ChildItem (Join-Path $root 'shared/agents') -Directory).Count
|
||||||
$sourceSkillCount = @(Get-ChildItem (Join-Path $root 'shared/skills') -Filter 'SKILL.md' -Recurse).Count
|
$sourceSkillCount = @(Get-ChildItem (Join-Path $root 'shared/skills') -Filter 'SKILL.md' -Recurse).Count
|
||||||
$ruleSkillCount = @(Import-Csv -LiteralPath (Join-Path $root 'adapters/claude/rule-skills.tsv') -Delimiter ([char]9)).Count
|
$ruleSkillCount = @(Import-Csv -LiteralPath (Join-Path $root 'adapters/claude/rule-skills.tsv') -Delimiter ([char]9)).Count
|
||||||
@@ -48,6 +50,9 @@ foreach ($platformSelection in @('1','2')) {
|
|||||||
if (($output -join "`n") -notmatch "-$platform") { throw 'Incorrect selected package.' }
|
if (($output -join "`n") -notmatch "-$platform") { throw 'Incorrect selected package.' }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
$doctorSummaryOutput = & (Join-Path $root 'scripts/doctor.ps1') -Summary
|
||||||
|
if (@($doctorSummaryOutput | Where-Object { $_ -match '^PASS ' }).Count -ne 0) { throw 'Doctor summary mode must not print individual PASS lines.' }
|
||||||
|
if (@($doctorSummaryOutput | Where-Object { $_ -match '^Doctor: PASS \| \d+ checks$' }).Count -ne 1) { throw "Doctor summary mode must print one 'Doctor: PASS | N checks' line: $($doctorSummaryOutput -join '; ')" }
|
||||||
Write-Output 'PASS four platform packages and six PowerShell selections'
|
Write-Output 'PASS four platform packages and six PowerShell selections'
|
||||||
exit 0
|
exit 0
|
||||||
|
|
||||||
@@ -6,7 +6,9 @@ grep -Eq '\$FadeMs[[:space:]]*=[[:space:]]*250' "$root/shared/hooks/flashbang.ps
|
|||||||
[ "$(grep -c 'default=250' "$root/shared/hooks/flashbang.sh")" -ge 2 ]
|
[ "$(grep -c 'default=250' "$root/shared/hooks/flashbang.sh")" -ge 2 ]
|
||||||
branch=$(git -C "$root" branch --show-current)
|
branch=$(git -C "$root" branch --show-current)
|
||||||
status_output=$(printf '{"model":{"display_name":"Test Model"},"effort":{"level":"high"},"workspace":{"current_dir":"%s","repo":{"name":"TestRepo"}},"context_window":{"context_window_size":200000,"used_percentage":8.5,"total_input_tokens":15500,"total_output_tokens":1200}}' "$root" | bash "$root/shared/statusline/statusline.sh")
|
status_output=$(printf '{"model":{"display_name":"Test Model"},"effort":{"level":"high"},"workspace":{"current_dir":"%s","repo":{"name":"TestRepo"}},"context_window":{"context_window_size":200000,"used_percentage":8.5,"total_input_tokens":15500,"total_output_tokens":1200}}' "$root" | bash "$root/shared/statusline/statusline.sh")
|
||||||
[ "$status_output" = "Model: Test Model | Effort: high | Repo: TestRepo | Branch: $branch | Max Context: 200000 | Used Context: 9% | Used Tokens: 16700" ]
|
plain_status_output=$(printf '%s' "$status_output" | sed -E $'s/\x1b\\[[0-9;]*m//g')
|
||||||
|
expected_status_output=$'Test Model \xc2\xb7 high\nTestRepo on '"$branch"$' \xc2\xb7 9% ctx (16.7k tok)'
|
||||||
|
[ "$plain_status_output" = "$expected_status_output" ]
|
||||||
source_agent_count=$(find "$root/shared/agents" -mindepth 1 -maxdepth 1 -type d | wc -l)
|
source_agent_count=$(find "$root/shared/agents" -mindepth 1 -maxdepth 1 -type d | wc -l)
|
||||||
source_skill_count=$(find "$root/shared/skills" -name SKILL.md | wc -l)
|
source_skill_count=$(find "$root/shared/skills" -name SKILL.md | wc -l)
|
||||||
rule_skill_count=$(($(wc -l < "$root/adapters/claude/rule-skills.tsv") - 1))
|
rule_skill_count=$(($(wc -l < "$root/adapters/claude/rule-skills.tsv") - 1))
|
||||||
@@ -54,5 +56,14 @@ for platform_selection in 1 2; do
|
|||||||
[[ "$output" == *"-$platform"* ]]
|
[[ "$output" == *"-$platform"* ]]
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
doctor_summary_output=$(bash "$root/scripts/doctor.sh" --summary)
|
||||||
|
if printf '%s\n' "$doctor_summary_output" | grep -q '^PASS '; then
|
||||||
|
printf 'Doctor summary mode must not print individual PASS lines.\n' >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if ! printf '%s\n' "$doctor_summary_output" | grep -Eq '^Doctor: PASS \| [0-9]+ checks$'; then
|
||||||
|
printf "Doctor summary mode must print one 'Doctor: PASS | N checks' line: %s\n" "$doctor_summary_output" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
printf 'PASS four platform packages and six Bash selections\n'
|
printf 'PASS four platform packages and six Bash selections\n'
|
||||||
|
|
||||||
Reference in new issue
Block a user