feat(install): ask for QMD local search models and run the QMD installer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Julian lechnerandClaude Opus 5.5 committed 2026-10-08 12:10:19 +02:00
1 parent 953401a431
commit 505e6ae084
10 files changed
+48 -18

No files matched your search

+2 -2
View File
@@ -12,7 +12,7 @@ $root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
. (Join-Path $root 'scripts/lib/install-targets.ps1') . (Join-Path $root 'scripts/lib/install-targets.ps1')
. (Join-Path $root 'scripts/lib/selection-state.ps1') . (Join-Path $root 'scripts/lib/selection-state.ps1')
. (Join-Path $root 'scripts/lib/install-options.ps1') . (Join-Path $root 'scripts/lib/install-options.ps1')
. (Join-Path $root 'scripts/lib/semantic-retrieval.ps1') . (Join-Path $root 'scripts/lib/qmd.ps1')
$Shell = Read-InstallShell -Shell $Shell $Shell = Read-InstallShell -Shell $Shell
$shell = $Shell.ToLowerInvariant() $shell = $Shell.ToLowerInvariant()
@@ -44,8 +44,8 @@ foreach ($item in (Get-InstallTargets -Generated $generated -HomePath $homePath
-AiConfigRoot $item.Destination.Replace('\','/') -ShellCommand $shellCommand -PowerShellCommand $powerShellCommand -ClaudeConcurrency $claudeConcurrency -FlashbangEnabled $options.flashbang -StatusLineEnabled $options.statusline -SemanticRetrievalEnabled $options.semantic_retrieval -DryRun:$DryRun -Summary:$Summary -AiConfigRoot $item.Destination.Replace('\','/') -ShellCommand $shellCommand -PowerShellCommand $powerShellCommand -ClaudeConcurrency $claudeConcurrency -FlashbangEnabled $options.flashbang -StatusLineEnabled $options.statusline -SemanticRetrievalEnabled $options.semantic_retrieval -DryRun:$DryRun -Summary:$Summary
} }
Sync-Qmd -RepositoryRoot $root -HomePath $homePath -Client $Client -Enabled $options.semantic_retrieval -DryRun:$DryRun -Summary:$Summary
Sync-ConfiguredPlugins -RepositoryRoot $root -HomePath $homePath -Client $Client -DryRun:$DryRun -Summary:$Summary -Entries $plugins.Selected Sync-ConfiguredPlugins -RepositoryRoot $root -HomePath $homePath -Client $Client -DryRun:$DryRun -Summary:$Summary -Entries $plugins.Selected
Sync-SemanticRetrieval -RepositoryRoot $root -HomePath $homePath -Client $Client -Enabled $options.semantic_retrieval -DryRun:$DryRun -Summary:$Summary
if (-not $DryRun) { Save-UpdateSelection -HomePath $homePath -RepositoryRoot $root -Shell $Shell -Client $Client -Plugins $plugins -Flashbang $options.flashbang -StatusLine $options.statusline -SemanticRetrieval $options.semantic_retrieval } if (-not $DryRun) { Save-UpdateSelection -HomePath $homePath -RepositoryRoot $root -Shell $Shell -Client $Client -Plugins $plugins -Flashbang $options.flashbang -StatusLine $options.statusline -SemanticRetrieval $options.semantic_retrieval }
if ($Summary) { Write-Output "Install: PASS | $Client, $Shell$(if ($DryRun) { ', dry-run' })" } else { Write-Output ($(if ($DryRun) { 'PASS install dry-run' } else { 'PASS install' })) } if ($Summary) { Write-Output "Install: PASS | $Client, $Shell$(if ($DryRun) { ', dry-run' })" } else { Write-Output ($(if ($DryRun) { 'PASS install dry-run' } else { 'PASS install' })) }
exit 0 exit 0
+2 -2
View File
@@ -21,7 +21,7 @@ root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
. "$root/scripts/lib/install-targets.sh" . "$root/scripts/lib/install-targets.sh"
. "$root/scripts/lib/selection-state.sh" . "$root/scripts/lib/selection-state.sh"
. "$root/scripts/lib/install-options.sh" . "$root/scripts/lib/install-options.sh"
. "$root/scripts/lib/semantic-retrieval.sh" . "$root/scripts/lib/qmd.sh"
shell=$(read_install_shell "$shell") shell=$(read_install_shell "$shell")
client=$(read_install_client "$client") client=$(read_install_client "$client")
@@ -61,8 +61,8 @@ while IFS='|' read -r source destination; do
sync_managed_destination "$source" "$destination" "$stamp" "$ai_config_root" "$shell_command" "$powershell_command" "$dry_run" "$summary" "$flashbang" "$claude_concurrency" "$statusline" "$semantic_retrieval" sync_managed_destination "$source" "$destination" "$stamp" "$ai_config_root" "$shell_command" "$powershell_command" "$dry_run" "$summary" "$flashbang" "$claude_concurrency" "$statusline" "$semantic_retrieval"
done < <(get_install_targets "$generated" "$home_path" "$shell" "$client") done < <(get_install_targets "$generated" "$home_path" "$shell" "$client")
sync_qmd "$root" "$client" "$home_path" "$semantic_retrieval" "$dry_run" false "$summary"
sync_configured_plugins "$root" "$client" "$home_path" "$dry_run" false selected_plugins "$summary" sync_configured_plugins "$root" "$client" "$home_path" "$dry_run" false selected_plugins "$summary"
sync_semantic_retrieval "$root" "$client" "$home_path" "$semantic_retrieval" "$dry_run" false "$summary"
if [ "$dry_run" = false ]; then save_update_selection; fi if [ "$dry_run" = false ]; then save_update_selection; fi
if [ "$summary" = true ]; then if [ "$summary" = true ]; then
+3 -3
View File
@@ -10,7 +10,7 @@ $root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
. (Join-Path $root 'scripts/lib/plugins.ps1') . (Join-Path $root 'scripts/lib/plugins.ps1')
. (Join-Path $root 'scripts/lib/manifest.ps1') . (Join-Path $root 'scripts/lib/manifest.ps1')
. (Join-Path $root 'scripts/lib/install-targets.ps1') . (Join-Path $root 'scripts/lib/install-targets.ps1')
. (Join-Path $root 'scripts/lib/semantic-retrieval.ps1') . (Join-Path $root 'scripts/lib/qmd.ps1')
$Client = Read-InstallClient -Client $Client $Client = Read-InstallClient -Client $Client
$homePath = [Environment]::GetFolderPath('UserProfile') $homePath = [Environment]::GetFolderPath('UserProfile')
@@ -24,7 +24,7 @@ if (-not $DryRun -and -not $Force) {
if ([Console]::IsInputRedirected -or $env:CI -eq 'true' -or $env:AI_CONFIG_NO_INTERACTIVE -eq '1') { if ([Console]::IsInputRedirected -or $env:CI -eq 'true' -or $env:AI_CONFIG_NO_INTERACTIVE -eq '1') {
throw 'Refusing to uninstall without confirmation in a non-interactive session. Pass -Force to proceed.' throw 'Refusing to uninstall without confirmation in a non-interactive session. Pass -Force to proceed.'
} }
Write-Host "This removes the managed configuration, extensions, and semantic retrieval setup for $Client under $homePath." Write-Host "This removes the managed configuration, extensions, and QMD local search setup for $Client under $homePath."
Write-Host 'Backups already on disk are kept; anything this setup never installed is left untouched.' Write-Host 'Backups already on disk are kept; anything this setup never installed is left untouched.'
$confirm = Read-Host -Prompt 'Continue? [y/N]' $confirm = Read-Host -Prompt 'Continue? [y/N]'
if ($null -eq $confirm) { $confirm = '' } if ($null -eq $confirm) { $confirm = '' }
@@ -57,7 +57,7 @@ try {
} }
Sync-ConfiguredPlugins -RepositoryRoot $root -HomePath $homePath -Client $Client -DryRun:$DryRun -Summary:$Summary -Entries @() Sync-ConfiguredPlugins -RepositoryRoot $root -HomePath $homePath -Client $Client -DryRun:$DryRun -Summary:$Summary -Entries @()
Sync-SemanticRetrieval -RepositoryRoot $root -HomePath $homePath -Client $Client -Enabled $false -DryRun:$DryRun -Summary:$Summary Sync-Qmd -RepositoryRoot $root -HomePath $homePath -Client $Client -Enabled $false -DryRun:$DryRun -Summary:$Summary
if (-not $DryRun) { if (-not $DryRun) {
$remaining = @($targets | Where-Object { Test-Path -LiteralPath (Get-ManagedManifestPath $_.Destination) }) $remaining = @($targets | Where-Object { Test-Path -LiteralPath (Get-ManagedManifestPath $_.Destination) })
+3 -3
View File
@@ -19,7 +19,7 @@ root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
. "$root/scripts/lib/plugins.sh" . "$root/scripts/lib/plugins.sh"
. "$root/scripts/lib/manifest.sh" . "$root/scripts/lib/manifest.sh"
. "$root/scripts/lib/install-targets.sh" . "$root/scripts/lib/install-targets.sh"
. "$root/scripts/lib/semantic-retrieval.sh" . "$root/scripts/lib/qmd.sh"
client=$(read_install_client "$client") client=$(read_install_client "$client")
home_path=${HOME:?HOME is required} home_path=${HOME:?HOME is required}
@@ -40,7 +40,7 @@ if [ "$dry_run" = false ] && [ "$force" = false ]; then
printf 'Refusing to uninstall without confirmation in a non-interactive session. Pass --force to proceed.\n' >&2 printf 'Refusing to uninstall without confirmation in a non-interactive session. Pass --force to proceed.\n' >&2
exit 1 exit 1
fi fi
printf 'This removes the managed configuration, extensions, and semantic retrieval setup for %s under %s.\n' "$client" "$home_path" printf 'This removes the managed configuration, extensions, and QMD local search setup for %s under %s.\n' "$client" "$home_path"
printf 'Backups already on disk are kept; anything this setup never installed is left untouched.\n' printf 'Backups already on disk are kept; anything this setup never installed is left untouched.\n'
read -r -p "Continue? [y/N]: " confirm || confirm="" read -r -p "Continue? [y/N]: " confirm || confirm=""
case "$confirm" in case "$confirm" in
@@ -75,7 +75,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
selected_plugins=() selected_plugins=()
sync_configured_plugins "$root" "$client" "$home_path" "$dry_run" false selected_plugins "$summary" sync_configured_plugins "$root" "$client" "$home_path" "$dry_run" false selected_plugins "$summary"
sync_semantic_retrieval "$root" "$client" "$home_path" false "$dry_run" false "$summary" sync_qmd "$root" "$client" "$home_path" false "$dry_run" false "$summary"
if [ "$dry_run" = false ]; then if [ "$dry_run" = false ]; then
remaining=0 remaining=0
+2 -2
View File
@@ -13,7 +13,7 @@ $root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent
. (Join-Path $root 'scripts/lib/install-targets.ps1') . (Join-Path $root 'scripts/lib/install-targets.ps1')
. (Join-Path $root 'scripts/lib/selection-state.ps1') . (Join-Path $root 'scripts/lib/selection-state.ps1')
. (Join-Path $root 'scripts/lib/install-options.ps1') . (Join-Path $root 'scripts/lib/install-options.ps1')
. (Join-Path $root 'scripts/lib/semantic-retrieval.ps1') . (Join-Path $root 'scripts/lib/qmd.ps1')
$homePath = [Environment]::GetFolderPath('UserProfile') $homePath = [Environment]::GetFolderPath('UserProfile')
if ($Quick) { if ($Quick) {
@@ -54,6 +54,7 @@ foreach ($item in (Get-InstallTargets -Generated $generated -HomePath $homePath
-AiConfigRoot $item.Destination.Replace('\','/') -ShellCommand $shellCommand -PowerShellCommand $powerShellCommand -FlashbangEnabled $options.flashbang -StatusLineEnabled $options.statusline -SemanticRetrievalEnabled $options.semantic_retrieval -DryRun:$DryRun -Summary:$Summary -AiConfigRoot $item.Destination.Replace('\','/') -ShellCommand $shellCommand -PowerShellCommand $powerShellCommand -FlashbangEnabled $options.flashbang -StatusLineEnabled $options.statusline -SemanticRetrievalEnabled $options.semantic_retrieval -DryRun:$DryRun -Summary:$Summary
} }
Sync-Qmd -RepositoryRoot $root -HomePath $homePath -Client $Client -Enabled $options.semantic_retrieval -DryRun:$DryRun -Update -Summary:$Summary
$previousPluginNonInteractive = $script:PluginNonInteractive $previousPluginNonInteractive = $script:PluginNonInteractive
$previousCI = $env:CI $previousCI = $env:CI
try { try {
@@ -64,7 +65,6 @@ try {
$script:PluginNonInteractive = $previousPluginNonInteractive $script:PluginNonInteractive = $previousPluginNonInteractive
$env:CI = $previousCI $env:CI = $previousCI
} }
Sync-SemanticRetrieval -RepositoryRoot $root -HomePath $homePath -Client $Client -Enabled $options.semantic_retrieval -DryRun:$DryRun -Update -Summary:$Summary
if (-not $DryRun) { Save-UpdateSelection -HomePath $homePath -RepositoryRoot $root -Shell $Shell -Client $Client -Plugins $plugins -Flashbang $options.flashbang -StatusLine $options.statusline -SemanticRetrieval $options.semantic_retrieval } if (-not $DryRun) { Save-UpdateSelection -HomePath $homePath -RepositoryRoot $root -Shell $Shell -Client $Client -Plugins $plugins -Flashbang $options.flashbang -StatusLine $options.statusline -SemanticRetrieval $options.semantic_retrieval }
if ($Summary) { Write-Output "Update: PASS | $Client, $Shell$(if ($DryRun) { ', dry-run' })" } else { Write-Output ($(if ($DryRun) { 'PASS update dry-run' } else { 'PASS update' })) } if ($Summary) { Write-Output "Update: PASS | $Client, $Shell$(if ($DryRun) { ', dry-run' })" } else { Write-Output ($(if ($DryRun) { 'PASS update dry-run' } else { 'PASS update' })) }
exit 0 exit 0
+2 -2
View File
@@ -23,7 +23,7 @@ root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)
. "$root/scripts/lib/install-targets.sh" . "$root/scripts/lib/install-targets.sh"
. "$root/scripts/lib/selection-state.sh" . "$root/scripts/lib/selection-state.sh"
. "$root/scripts/lib/install-options.sh" . "$root/scripts/lib/install-options.sh"
. "$root/scripts/lib/semantic-retrieval.sh" . "$root/scripts/lib/qmd.sh"
home_path=${HOME:?HOME is required} home_path=${HOME:?HOME is required}
selected_plugins=() selected_plugins=()
@@ -66,12 +66,12 @@ while IFS='|' read -r source destination; do
sync_managed_destination "$source" "$destination" "$stamp" "$ai_config_root" "$shell_command" "$powershell_command" "$dry_run" "$summary" "$flashbang" 5 "$statusline" "$semantic_retrieval" sync_managed_destination "$source" "$destination" "$stamp" "$ai_config_root" "$shell_command" "$powershell_command" "$dry_run" "$summary" "$flashbang" 5 "$statusline" "$semantic_retrieval"
done < <(get_install_targets "$generated" "$home_path" "$shell" "$client") done < <(get_install_targets "$generated" "$home_path" "$shell" "$client")
sync_qmd "$root" "$client" "$home_path" "$semantic_retrieval" "$dry_run" true "$summary"
if [ "$quick" = true ]; then if [ "$quick" = true ]; then
CI=true sync_configured_plugins "$root" "$client" "$home_path" "$dry_run" true selected_plugins "$summary" </dev/null CI=true sync_configured_plugins "$root" "$client" "$home_path" "$dry_run" true selected_plugins "$summary" </dev/null
else else
sync_configured_plugins "$root" "$client" "$home_path" "$dry_run" true selected_plugins "$summary" sync_configured_plugins "$root" "$client" "$home_path" "$dry_run" true selected_plugins "$summary"
fi fi
sync_semantic_retrieval "$root" "$client" "$home_path" "$semantic_retrieval" "$dry_run" true "$summary"
if [ "$dry_run" = false ]; then save_update_selection; fi if [ "$dry_run" = false ]; then save_update_selection; fi
if [ "$summary" = true ]; then if [ "$summary" = true ]; then
+2 -2
View File
@@ -32,14 +32,14 @@ function Read-SemanticRetrievalOption {
param([bool]$Default,[string]$RepositoryRoot,[string]$HomePath) param([bool]$Default,[string]$RepositoryRoot,[string]$HomePath)
$hint = if ($Default) { 'Y/n/a' } else { 'y/N/a' } $hint = if ($Default) { 'Y/n/a' } else { 'y/N/a' }
while ($true) { while ($true) {
Write-Host "Enable Qwen3 embedding and reranking semantic retrieval? [$hint] " -NoNewline Write-Host "Enable local QMD search models (Qwen3 embedding, Qwen3 reranker, QMD query expansion)? [$hint] " -NoNewline
$answer = [Console]::ReadLine() $answer = [Console]::ReadLine()
if ($null -eq $answer) { throw 'Input ended before semantic retrieval was confirmed.' } if ($null -eq $answer) { throw 'Input ended before semantic retrieval was confirmed.' }
switch ($answer.Trim().ToLowerInvariant()) { switch ($answer.Trim().ToLowerInvariant()) {
'' { return $Default } '' { return $Default }
{ $_ -in @('y', 'yes') } { return $true } { $_ -in @('y', 'yes') } { return $true }
{ $_ -in @('n', 'no') } { return $false } { $_ -in @('n', 'no') } { return $false }
{ $_ -in @('a', 'auto') } { return Test-SemanticRetrievalDevice -RepositoryRoot $RepositoryRoot -HomePath $HomePath } { $_ -in @('a', 'auto') } { return Test-QmdDevice -RepositoryRoot $RepositoryRoot -HomePath $HomePath }
} }
} }
} }
+2 -2
View File
@@ -41,7 +41,7 @@ read_semantic_retrieval_option() {
local default=$1 hint=y/N/a answer local default=$1 hint=y/N/a answer
[ "$default" = false ] || hint=Y/n/a [ "$default" = false ] || hint=Y/n/a
while :; do while :; do
printf 'Enable Qwen3 embedding and reranking semantic retrieval? [%s] ' "$hint" >&2 printf 'Enable local QMD search models (Qwen3 embedding, Qwen3 reranker, QMD query expansion)? [%s] ' "$hint" >&2
IFS= read -r answer || { printf 'Input ended before semantic retrieval was confirmed.\n' >&2; return 1; } IFS= read -r answer || { printf 'Input ended before semantic retrieval was confirmed.\n' >&2; return 1; }
answer=${answer%$'\r'} answer=${answer%$'\r'}
case "$answer" in case "$answer" in
@@ -49,7 +49,7 @@ read_semantic_retrieval_option() {
y|Y|yes|Yes|YES) printf 'true\n'; return ;; y|Y|yes|Yes|YES) printf 'true\n'; return ;;
n|N|no|No|NO) printf 'false\n'; return ;; n|N|no|No|NO) printf 'false\n'; return ;;
a|A|auto|Auto|AUTO) a|A|auto|Auto|AUTO)
if test_semantic_retrieval_device "$root" "$home_path"; then printf 'true\n'; return; else status=$?; fi if test_qmd_device "$root" "$home_path"; then printf 'true\n'; return; else status=$?; fi
[ "$status" -eq 1 ] && { printf 'false\n'; return; } [ "$status" -eq 1 ] && { printf 'false\n'; return; }
return "$status" return "$status"
;; ;;
+16
View File
@@ -17,6 +17,22 @@ try {
if (Test-AllManifestsPresent @($installed, $missing)) { throw 'Update guard must fail when any selected destination is not installed.' } if (Test-AllManifestsPresent @($installed, $missing)) { throw 'Update guard must fail when any selected destination is not installed.' }
if (-not (Test-AllManifestsPresent @($installed))) { throw 'Update guard must pass when every selected destination is installed.' } if (-not (Test-AllManifestsPresent @($installed))) { throw 'Update guard must pass when every selected destination is installed.' }
# QMD prompt: Auto maps device suitability; No never calls the benchmark.
. (Join-Path $root 'scripts/lib/install-options.ps1')
$originalIn = [Console]::In
try {
function Test-QmdDevice { param($RepositoryRoot, $HomePath) return $false }
[Console]::SetIn([IO.StringReader]::new("a`n"))
if ((Read-SemanticRetrievalOption -Default $false -RepositoryRoot $root -HomePath $work) -ne $false) { throw 'Auto unsuitable must yield false' }
function Test-QmdDevice { param($RepositoryRoot, $HomePath) return $true }
[Console]::SetIn([IO.StringReader]::new("a`n"))
if ((Read-SemanticRetrievalOption -Default $false -RepositoryRoot $root -HomePath $work) -ne $true) { throw 'Auto suitable must yield true' }
function Test-QmdDevice { throw 'must not run' }
[Console]::SetIn([IO.StringReader]::new("n`n"))
if ((Read-SemanticRetrievalOption -Default $true -RepositoryRoot $root -HomePath $work) -ne $false) { throw 'No must yield false' }
} finally { [Console]::SetIn($originalIn) }
if ((Get-Content -Raw (Join-Path $root 'scripts/lib/install-options.ps1')) -notmatch 'Enable local QMD search models \(Qwen3 embedding, Qwen3 reranker, QMD query expansion\)\? \[') { throw 'prompt text changed' }
if ($Summary) { Write-Output 'Tests: PASS | install guards' } else { Write-Output 'PASS install guards: already-installed and not-installed detection' } if ($Summary) { Write-Output 'Tests: PASS | install guards' } else { Write-Output 'PASS install guards: already-installed and not-installed detection' }
} finally { } finally {
Remove-Item $work -Recurse -Force -ErrorAction SilentlyContinue Remove-Item $work -Recurse -Force -ErrorAction SilentlyContinue
+14
View File
@@ -25,4 +25,18 @@ any_manifest_present "$installed" "$missing" || { printf 'Expected an installed
! all_manifests_present "$installed" "$missing" || { printf 'Update guard must fail when any selected destination is not installed.\n' >&2; exit 1; } ! all_manifests_present "$installed" "$missing" || { printf 'Update guard must fail when any selected destination is not installed.\n' >&2; exit 1; }
all_manifests_present "$installed" || { printf 'Update guard must pass when every selected destination is installed.\n' >&2; exit 1; } all_manifests_present "$installed" || { printf 'Update guard must pass when every selected destination is installed.\n' >&2; exit 1; }
# QMD prompt: Auto maps benchmark exit codes; No never calls the benchmark.
(
home_path=$(mktemp -d)
. "$root/scripts/lib/install-options.sh"
test_qmd_device() { return 1; }
[ "$(printf 'a\n' | read_semantic_retrieval_option false 2>/dev/null)" = false ] || { echo 'Auto unsuitable must yield false' >&2; exit 1; }
test_qmd_device() { return 0; }
[ "$(printf 'a\n' | read_semantic_retrieval_option false 2>/dev/null)" = true ] || { echo 'Auto suitable must yield true' >&2; exit 1; }
test_qmd_device() { echo 'must not run' >&2; exit 9; }
[ "$(printf 'n\n' | read_semantic_retrieval_option true 2>/dev/null)" = false ] || { echo 'No must yield false' >&2; exit 1; }
printf 'n\n' | read_semantic_retrieval_option true 2>&1 >/dev/null | grep -q 'Enable local QMD search models' || { echo 'prompt text changed' >&2; exit 1; }
rm -rf -- "$home_path"
)
if [ "$summary" = true ]; then printf 'Tests: PASS | install guards\n'; else printf 'PASS install guards: already-installed and not-installed detection\n'; fi if [ "$summary" = true ]; then printf 'Tests: PASS | install guards\n'; else printf 'PASS install guards: already-installed and not-installed detection\n'; fi