fix(install): exchange install option content through UTF-8 files

Windows PowerShell pipes re-encode text by console code page: under code page
65001 a BOM broke the config.toml merge, and otherwise non-ASCII characters
became question marks. Pass managed and merged content through files, and
decode piped input as utf-8-sig.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Julian lechnerandClaude Opus 5.5 committed 2026-10-01 10:23:14 +02:00
1 parent bed90fee2d
commit 857720bcfd
3 files changed
+51 -12

No files matched your search

+12 -6
View File
@@ -173,18 +173,24 @@ if __name__ == '__main__':
parser.add_argument('--current', type=Path)
parser.add_argument('--flashbang', choices=('true', 'false'))
parser.add_argument('--statusline', choices=('true', 'false'), default='true')
parser.add_argument('--managed', type=Path)
parser.add_argument('--output', type=Path)
args = parser.parse_args()
try:
sys.stdout.reconfigure(encoding='utf-8')
if args.action == 'filter':
print(filter_options(args.path, args.flashbang == 'true', args.statusline == 'true'), end='')
result = filter_options(args.path, args.flashbang == 'true', args.statusline == 'true')
else:
managed = args.managed.read_text(encoding='utf-8-sig') if args.managed else sys.stdin.buffer.read().decode('utf-8-sig')
current = args.current.read_text(encoding='utf-8-sig')
if args.action == 'merge-json':
current = json.loads(args.current.read_text(encoding='utf-8-sig'))
managed = json.load(sys.stdin)
print(json.dumps(merge_json(current, managed), indent=2, ensure_ascii=False) + '\n', end='')
result = json.dumps(merge_json(json.loads(current), json.loads(managed)), indent=2, ensure_ascii=False) + '\n'
else:
print(merge_toml(args.current.read_text(encoding='utf-8-sig'), sys.stdin.read(), args.statusline == 'true'), end='')
result = merge_toml(current, managed, args.statusline == 'true')
if args.output:
args.output.write_text(result, encoding='utf-8', newline='')
else:
sys.stdout.reconfigure(encoding='utf-8')
print(result, end='')
except (OSError, ValueError, KeyError) as error:
print(f'Install options: {error}', file=sys.stderr)
sys.exit(1)
+23 -6
View File
@@ -86,6 +86,26 @@ function Write-ManagedManifest {
[System.IO.File]::WriteAllText($ManifestPath, $content, (New-Object System.Text.UTF8Encoding($false)))
}
function Invoke-InstallOptions {
param([Parameter(Mandatory=$true)][string[]]$Arguments, [byte[]]$Managed, [Parameter(Mandatory=$true)][string]$ErrorMessage)
# Exchange content through UTF-8 files: Windows PowerShell pipes re-encode text by console code page.
$outputPath = [System.IO.Path]::GetTempFileName()
$managedPath = $null
try {
if ($null -ne $Managed) {
$managedPath = [System.IO.Path]::GetTempFileName()
[System.IO.File]::WriteAllBytes($managedPath, $Managed)
$Arguments += @('--managed', $managedPath)
}
& python (Join-Path $PSScriptRoot 'install-options.py') @Arguments --output $outputPath
if ($LASTEXITCODE -ne 0) { throw $ErrorMessage }
return [System.IO.File]::ReadAllText($outputPath, (New-Object System.Text.UTF8Encoding($false)))
} finally {
Remove-Item -LiteralPath $outputPath -Force -ErrorAction SilentlyContinue
if ($managedPath) { Remove-Item -LiteralPath $managedPath -Force -ErrorAction SilentlyContinue }
}
}
function Sync-ManagedDestination {
<#
.SYNOPSIS
@@ -135,8 +155,7 @@ function Sync-ManagedDestination {
if ($filterOptions) {
$flashbangOption = if ($FlashbangEnabled) { 'true' } else { 'false' }
$statusLineOption = if ($StatusLineEnabled) { 'true' } else { 'false' }
$rawContent = (& python (Join-Path $PSScriptRoot 'install-options.py') filter --path $_.FullName --flashbang $flashbangOption --statusline $statusLineOption | Out-String)
if ($LASTEXITCODE -ne 0) { throw 'Could not configure install options.' }
$rawContent = Invoke-InstallOptions -Arguments @('filter', '--path', $_.FullName, '--flashbang', $flashbangOption, '--statusline', $statusLineOption) -ErrorMessage 'Could not configure install options.'
}
$needsSubstitution = $filterOptions -or $rawContent.Contains('__AI_CONFIG_ROOT__') -or $rawContent.Contains('__HOOK_COMMAND__') -or $rawContent.Contains('__POWERSHELL_HOOK_COMMAND__') -or $rawContent.Contains('__POWERSHELL_COMMAND__') -or $rawContent.Contains('__CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY__')
if ($needsSubstitution) {
@@ -147,13 +166,11 @@ function Sync-ManagedDestination {
}
if ($relative -eq 'config.toml' -and (Test-Path -LiteralPath $target)) {
$statusLineFlag = if ($StatusLineEnabled) { 'true' } else { 'false' }
$content = [System.Text.Encoding]::UTF8.GetString($newBytes) | & python (Join-Path $PSScriptRoot 'install-options.py') merge-toml --current $target --statusline $statusLineFlag | Out-String
if ($LASTEXITCODE -ne 0) { throw 'Could not merge managed config.toml keys.' }
$content = Invoke-InstallOptions -Arguments @('merge-toml', '--current', $target, '--statusline', $statusLineFlag) -Managed $newBytes -ErrorMessage 'Could not merge managed config.toml keys.'
$newBytes = [System.Text.Encoding]::UTF8.GetBytes($content)
}
if ($relative -eq 'settings.json' -and (Test-Path -LiteralPath $target)) {
$finalContent = [System.Text.Encoding]::UTF8.GetString($newBytes) | & python (Join-Path $PSScriptRoot 'install-options.py') merge-json --current $target | Out-String
if ($LASTEXITCODE -ne 0) { throw 'Could not merge managed settings.json keys.' }
$finalContent = Invoke-InstallOptions -Arguments @('merge-json', '--current', $target) -Managed $newBytes -ErrorMessage 'Could not merge managed settings.json keys.'
$newBytes = [System.Text.Encoding]::UTF8.GetBytes($finalContent)
}
$newHash = Get-Sha256HashOfBytes $newBytes