This guide applies to the publication-candidate artifact targeting 0.1.0-rc.1. Bundled text does
not prove current publication or marketplace availability. Use host-managed lifecycle controls only
for a trusted listing visible to the current account; otherwise use the receipt-checked preview
procedure below.
The preview installer writes .structured-thinking-installation into every installed Skill. The
lifecycle script refuses to move a directory when its receipt is missing, is a symbolic link, or
does not match the product, Skill name, version, source commit, source-tree digest, scope, and
absolute discovery path. It never deletes the shared skills/ root or an unrelated Skill.
An uninstall moves all three verified directories together into .agents/.structured-thinking-backups/,
outside the Skill discovery root. This is deliberately reversible. A restore requires the selected
discovery root to be empty for all three names and accepts only a receipt-checked backup directly
under that backup root.
Save the appropriate block outside every discovery and backup root. Run it as a script so an unhandled error produces a native non-zero process exit.
Save as /tmp/structured-thinking-preview-lifecycle.sh:
#!/usr/bin/env bash
set -Eeuo pipefail
EXPECTED_VERSION="0.1.0-rc.1"
SKILLS=(structured-thinking decision-analysis incident-analysis)
ACTION="${1:-}"
SCOPE="${2:-}"
REPO_INPUT="${3:-}"
BACKUP_INPUT="${4:-}"
die() {
printf 'ERROR: %s\n' "$*" >&2
exit 1
}
[[ "$ACTION" == "inspect" || "$ACTION" == "uninstall" || "$ACTION" == "restore" ||
"$ACTION" == "purge" ]] ||
die "usage: $0 inspect|uninstall user | $0 inspect|uninstall repository REPO_ROOT | $0 restore|purge user '' BACKUP_ROOT | $0 restore|purge repository REPO_ROOT BACKUP_ROOT"
[[ "$SCOPE" == "user" || "$SCOPE" == "repository" ]] || die "scope must be user or repository"
if [[ "$SCOPE" == "user" ]]; then
AGENT_ROOT="$HOME/.agents"
else
[[ -n "$REPO_INPUT" ]] || die "REPO_ROOT is required for repository scope"
REPO_ROOT="$(cd "$REPO_INPUT" && pwd -P)"
[[ "$REPO_ROOT" != *$'\n'* && "$REPO_ROOT" != *$'\r'* ]] ||
die "REPO_ROOT must not contain CR or LF"
AGENT_ROOT="$REPO_ROOT/.agents"
fi
[[ -d "$AGENT_ROOT" && ! -L "$AGENT_ROOT" ]] || die "invalid Agent root: $AGENT_ROOT"
AGENT_ROOT="$(cd "$AGENT_ROOT" && pwd -P)"
[[ "$AGENT_ROOT" != *$'\n'* && "$AGENT_ROOT" != *$'\r'* ]] ||
die "Agent root must not contain CR or LF"
SKILL_ROOT="$AGENT_ROOT/skills"
[[ -d "$SKILL_ROOT" && ! -L "$SKILL_ROOT" ]] || die "invalid Skill root: $SKILL_ROOT"
SKILL_ROOT="$(cd "$SKILL_ROOT" && pwd -P)"
[[ "$SKILL_ROOT" != *$'\n'* && "$SKILL_ROOT" != *$'\r'* ]] ||
die "Skill root must not contain CR or LF"
BACKUP_BASE="$AGENT_ROOT/.structured-thinking-backups"
prepare_backup_base() {
if [[ -L "$BACKUP_BASE" ]]; then
die "backup base must not be a symbolic link: $BACKUP_BASE"
fi
if [[ -e "$BACKUP_BASE" && ! -d "$BACKUP_BASE" ]]; then
die "backup base is not a directory: $BACKUP_BASE"
fi
mkdir -p "$BACKUP_BASE"
BACKUP_BASE="$(cd "$BACKUP_BASE" && pwd -P)"
[[ "$(dirname "$BACKUP_BASE")" == "$AGENT_ROOT" ]] ||
die "backup base escaped Agent root: $BACKUP_BASE"
[[ "$BACKUP_BASE" != *$'\n'* && "$BACKUP_BASE" != *$'\r'* ]] ||
die "backup base must not contain CR or LF"
}
valid_receipt() {
local actual="$1"
local skill="$2"
local expected_path="$3"
local receipt="$actual/.structured-thinking-installation"
[[ -d "$actual" && ! -L "$actual" && -f "$receipt" && ! -L "$receipt" ]] &&
grep -Fqx "receipt_format=1" "$receipt" &&
grep -Fqx "project=structured-thinking" "$receipt" &&
grep -Fqx "skill=$skill" "$receipt" &&
grep -Fqx "version=$EXPECTED_VERSION" "$receipt" &&
grep -Eq '^source_commit=[0-9a-fA-F]{40,64}$' "$receipt" &&
grep -Eq '^source_tree=[0-9a-fA-F]{40,64}$' "$receipt" &&
grep -Fqx "scope=$SCOPE" "$receipt" &&
grep -Fqx "installed_path=$expected_path" "$receipt" &&
grep -Eq '^installed_at=[0-9]{4}-[0-9]{2}-[0-9]{2}T' "$receipt"
}
if [[ "$ACTION" == "inspect" ]]; then
for skill in "${SKILLS[@]}"; do
destination="$SKILL_ROOT/$skill"
valid_receipt "$destination" "$skill" "$destination" ||
die "missing or mismatched receipt: $destination"
printf '%s\n' "$destination"
grep -E '^(version|source_commit|source_tree|scope|installed_path|installed_at)=' \
"$destination/.structured-thinking-installation"
done
exit 0
fi
LOCK_DIR="$AGENT_ROOT/.structured-thinking-install.lock"
mkdir "$LOCK_DIR" 2>/dev/null || die "another install or lifecycle operation may be active: $LOCK_DIR"
MODE=""
ACTIVE_BACKUP=""
MOVED=()
cleanup() {
local status=$?
trap - EXIT
if (( status != 0 )); then
if [[ "$MODE" == "uninstall" ]]; then
for skill in "${MOVED[@]}"; do
source="$ACTIVE_BACKUP/$skill"
destination="$SKILL_ROOT/$skill"
if valid_receipt "$source" "$skill" "$destination" &&
[[ ! -e "$destination" && ! -L "$destination" ]]; then
mv -n "$source" "$destination"
if [[ -e "$source" || -L "$source" ]]; then
printf 'REFUSED late collision while restoring %s\n' "$skill" >&2
fi
else
printf 'REFUSED automatic restore for %s\n' "$skill" >&2
fi
done
elif [[ "$MODE" == "restore" ]]; then
mkdir -p "$ACTIVE_BACKUP"
for skill in "${MOVED[@]}"; do
source="$SKILL_ROOT/$skill"
destination="$ACTIVE_BACKUP/$skill"
if valid_receipt "$source" "$skill" "$SKILL_ROOT/$skill" &&
[[ ! -e "$destination" && ! -L "$destination" ]]; then
mv -n "$source" "$destination"
if [[ -e "$source" || -L "$source" ]]; then
printf 'REFUSED late collision while repairing backup %s\n' "$skill" >&2
fi
else
printf 'REFUSED automatic backup repair for %s\n' "$skill" >&2
fi
done
fi
fi
if [[ -n "$ACTIVE_BACKUP" && -d "$ACTIVE_BACKUP" && ! -L "$ACTIVE_BACKUP" ]]; then
rmdir "$ACTIVE_BACKUP" 2>/dev/null || true
fi
rmdir "$LOCK_DIR" 2>/dev/null || true
exit "$status"
}
trap cleanup EXIT
if [[ "$ACTION" == "uninstall" ]]; then
for skill in "${SKILLS[@]}"; do
destination="$SKILL_ROOT/$skill"
valid_receipt "$destination" "$skill" "$destination" ||
die "missing or mismatched receipt: $destination"
done
prepare_backup_base
ACTIVE_BACKUP="$(mktemp -d "$BACKUP_BASE/backup.XXXXXX")"
MODE="uninstall"
for skill in "${SKILLS[@]}"; do
mv -n "$SKILL_ROOT/$skill" "$ACTIVE_BACKUP/$skill"
[[ ! -e "$SKILL_ROOT/$skill" && ! -L "$SKILL_ROOT/$skill" ]] ||
die "no-clobber move refused backup destination: $ACTIVE_BACKUP/$skill"
MOVED+=("$skill")
done
MODE=""
printf '%s\n' "$ACTIVE_BACKUP"
ACTIVE_BACKUP=""
exit 0
fi
[[ -n "$BACKUP_INPUT" ]] || die "BACKUP_ROOT is required for restore or purge"
prepare_backup_base
[[ -d "$BACKUP_INPUT" && ! -L "$BACKUP_INPUT" ]] || die "invalid backup root: $BACKUP_INPUT"
ACTIVE_BACKUP="$(cd "$BACKUP_INPUT" && pwd -P)"
[[ "$(dirname "$ACTIVE_BACKUP")" == "$BACKUP_BASE" ]] ||
die "backup must be a direct child of $BACKUP_BASE"
for skill in "${SKILLS[@]}"; do
destination="$SKILL_ROOT/$skill"
valid_receipt "$ACTIVE_BACKUP/$skill" "$skill" "$destination" ||
die "missing or mismatched backup receipt: $ACTIVE_BACKUP/$skill"
done
if [[ "$ACTION" == "purge" ]]; then
entry_count="$(find "$ACTIVE_BACKUP" -mindepth 1 -maxdepth 1 -print | wc -l | tr -d '[:space:]')"
[[ "$entry_count" == "${#SKILLS[@]}" ]] ||
die "backup contains unexpected entries: $ACTIVE_BACKUP"
for skill in "${SKILLS[@]}"; do
rm -rf -- "$ACTIVE_BACKUP/$skill"
done
rmdir "$ACTIVE_BACKUP"
printf 'Purged receipt-checked backup %s\n' "$ACTIVE_BACKUP"
ACTIVE_BACKUP=""
exit 0
fi
for skill in "${SKILLS[@]}"; do
destination="$SKILL_ROOT/$skill"
if [[ -e "$destination" || -L "$destination" ]]; then
die "restore destination is not empty: $destination"
fi
done
MODE="restore"
for skill in "${SKILLS[@]}"; do
mv -n "$ACTIVE_BACKUP/$skill" "$SKILL_ROOT/$skill"
[[ ! -e "$ACTIVE_BACKUP/$skill" && ! -L "$ACTIVE_BACKUP/$skill" ]] ||
die "no-clobber move refused late collision: $SKILL_ROOT/$skill"
MOVED+=("$skill")
done
rmdir "$ACTIVE_BACKUP"
MODE=""
ACTIVE_BACKUP=""
printf 'Restored %s into %s\n' "$EXPECTED_VERSION" "$SKILL_ROOT"Save as $env:TEMP/structured-thinking-preview-lifecycle.ps1:
param(
[Parameter(Mandatory = $true)]
[ValidateSet("inspect", "uninstall", "restore", "purge")]
[string]$Action,
[Parameter(Mandatory = $true)]
[ValidateSet("user", "repository")]
[string]$Scope,
[string]$RepoRoot,
[string]$BackupRoot
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$expectedVersion = "0.1.0-rc.1"
$skills = @("structured-thinking", "decision-analysis", "incident-analysis")
$moved = [System.Collections.Generic.List[string]]::new()
$mode = ""
$activeBackup = $null
$lockDir = $null
function Assert-NoLineBreak([string]$Name, [string]$Value) {
if ($Value.Contains("`r") -or $Value.Contains("`n")) { throw "$Name must not contain CR or LF" }
}
function Get-ExistingEntry([string]$Path) {
Get-Item -LiteralPath $Path -Force -ErrorAction SilentlyContinue
}
if ($Scope -eq "user") {
$agentRoot = Join-Path $HOME ".agents"
} else {
if ([string]::IsNullOrWhiteSpace($RepoRoot)) { throw "RepoRoot is required for repository scope" }
$resolvedRepoRoot = (Resolve-Path -LiteralPath $RepoRoot).Path
Assert-NoLineBreak "RepoRoot" $resolvedRepoRoot
$agentRoot = Join-Path $resolvedRepoRoot ".agents"
}
$agentEntry = Get-ExistingEntry $agentRoot
if ($null -eq $agentEntry -or -not $agentEntry.PSIsContainer -or
($agentEntry.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
throw "Invalid Agent root: $agentRoot"
}
$agentRoot = (Resolve-Path -LiteralPath $agentRoot).Path
Assert-NoLineBreak "Agent root" $agentRoot
$skillRoot = Join-Path $agentRoot "skills"
$skillEntry = Get-ExistingEntry $skillRoot
if ($null -eq $skillEntry -or -not $skillEntry.PSIsContainer -or
($skillEntry.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
throw "Invalid Skill root: $skillRoot"
}
$skillRoot = (Resolve-Path -LiteralPath $skillRoot).Path
Assert-NoLineBreak "Skill root" $skillRoot
$backupBase = Join-Path $agentRoot ".structured-thinking-backups"
function Resolve-BackupBase {
$entry = Get-ExistingEntry $backupBase
if ($null -ne $entry -and
(($entry.Attributes -band [IO.FileAttributes]::ReparsePoint) -or -not $entry.PSIsContainer)) {
throw "Backup base must be a real directory: $backupBase"
}
New-Item -ItemType Directory -Force -Path $backupBase | Out-Null
$resolved = (Resolve-Path -LiteralPath $backupBase).Path
Assert-NoLineBreak "Backup base" $resolved
if ((Split-Path $resolved -Parent) -ne $agentRoot) {
throw "Backup base escaped Agent root: $resolved"
}
$resolved
}
function Test-Receipt([string]$Actual, [string]$Skill, [string]$ExpectedPath) {
$entry = Get-ExistingEntry $Actual
if ($null -eq $entry -or -not $entry.PSIsContainer -or
($entry.Attributes -band [IO.FileAttributes]::ReparsePoint)) { return $false }
$receipt = Join-Path $Actual ".structured-thinking-installation"
$receiptEntry = Get-ExistingEntry $receipt
if ($null -eq $receiptEntry -or $receiptEntry.PSIsContainer -or
($receiptEntry.Attributes -band [IO.FileAttributes]::ReparsePoint)) { return $false }
$lines = @(Get-Content -LiteralPath $receipt)
foreach ($expected in @(
"receipt_format=1",
"project=structured-thinking",
"skill=$Skill",
"version=$expectedVersion",
"scope=$Scope",
"installed_path=$ExpectedPath"
)) {
if ($lines -cnotcontains $expected) { return $false }
}
$commit = $lines | Where-Object { $_ -cmatch '^source_commit=[0-9a-fA-F]{40,64}$' }
$tree = $lines | Where-Object { $_ -cmatch '^source_tree=[0-9a-fA-F]{40,64}$' }
$time = $lines | Where-Object { $_ -cmatch '^installed_at=[0-9]{4}-[0-9]{2}-[0-9]{2}T' }
($null -ne $commit) -and ($null -ne $tree) -and ($null -ne $time)
}
if ($Action -eq "inspect") {
foreach ($skill in $skills) {
$destination = [IO.Path]::GetFullPath((Join-Path $skillRoot $skill))
if (-not (Test-Receipt $destination $skill $destination)) {
throw "Missing or mismatched receipt: $destination"
}
Write-Output $destination
Get-Content -LiteralPath (Join-Path $destination ".structured-thinking-installation") |
Where-Object { $_ -match '^(version|source_commit|source_tree|scope|installed_path|installed_at)=' }
}
exit 0
}
$lockDir = Join-Path $agentRoot ".structured-thinking-install.lock"
if ($null -ne (Get-ExistingEntry $lockDir)) {
throw "Another install or lifecycle operation may be active: $lockDir"
}
New-Item -ItemType Directory -Path $lockDir | Out-Null
try {
if ($Action -eq "uninstall") {
foreach ($skill in $skills) {
$destination = [IO.Path]::GetFullPath((Join-Path $skillRoot $skill))
if (-not (Test-Receipt $destination $skill $destination)) {
throw "Missing or mismatched receipt: $destination"
}
}
$backupBase = Resolve-BackupBase
$activeBackup = Join-Path $backupBase ("backup." + [guid]::NewGuid())
New-Item -ItemType Directory -Path $activeBackup | Out-Null
$mode = "uninstall"
foreach ($skill in $skills) {
[IO.Directory]::Move((Join-Path $skillRoot $skill), (Join-Path $activeBackup $skill))
$moved.Add($skill)
}
$mode = ""
Write-Output $activeBackup
$activeBackup = $null
} else {
if ([string]::IsNullOrWhiteSpace($BackupRoot)) { throw "BackupRoot is required for restore or purge" }
$backupBase = Resolve-BackupBase
$backupEntry = Get-ExistingEntry $BackupRoot
if ($null -eq $backupEntry -or -not $backupEntry.PSIsContainer -or
($backupEntry.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
throw "Invalid backup root: $BackupRoot"
}
$activeBackup = (Resolve-Path -LiteralPath $BackupRoot).Path
if ((Split-Path $activeBackup -Parent) -ne $backupBase) {
throw "Backup must be a direct child of $backupBase"
}
foreach ($skill in $skills) {
$destination = [IO.Path]::GetFullPath((Join-Path $skillRoot $skill))
if (-not (Test-Receipt (Join-Path $activeBackup $skill) $skill $destination)) {
throw "Missing or mismatched backup receipt: $activeBackup/$skill"
}
}
if ($Action -eq "purge") {
$entries = @(Get-ChildItem -LiteralPath $activeBackup -Force)
if ($entries.Count -ne $skills.Count) {
throw "Backup contains unexpected entries: $activeBackup"
}
foreach ($skill in $skills) {
Remove-Item -Recurse -Force -LiteralPath (Join-Path $activeBackup $skill)
}
Remove-Item -LiteralPath $activeBackup
Write-Host "Purged receipt-checked backup $activeBackup"
$activeBackup = $null
return
}
foreach ($skill in $skills) {
$destination = [IO.Path]::GetFullPath((Join-Path $skillRoot $skill))
if ($null -ne (Get-ExistingEntry $destination)) {
throw "Restore destination is not empty: $destination"
}
}
$mode = "restore"
foreach ($skill in $skills) {
[IO.Directory]::Move((Join-Path $activeBackup $skill), (Join-Path $skillRoot $skill))
$moved.Add($skill)
}
Remove-Item -LiteralPath $activeBackup
$mode = ""
$activeBackup = $null
Write-Host "Restored $expectedVersion into $skillRoot"
}
} catch {
if ($mode -eq "uninstall") {
foreach ($skill in $moved) {
$source = Join-Path $activeBackup $skill
$destination = [IO.Path]::GetFullPath((Join-Path $skillRoot $skill))
if ((Test-Receipt $source $skill $destination) -and
($null -eq (Get-ExistingEntry $destination))) {
[IO.Directory]::Move($source, $destination)
} else {
Write-Error "REFUSED automatic restore for $skill" -ErrorAction Continue
}
}
} elseif ($mode -eq "restore") {
New-Item -ItemType Directory -Force -Path $activeBackup | Out-Null
foreach ($skill in $moved) {
$source = Join-Path $skillRoot $skill
$destination = Join-Path $activeBackup $skill
if ((Test-Receipt $source $skill ([IO.Path]::GetFullPath($source))) -and
($null -eq (Get-ExistingEntry $destination))) {
[IO.Directory]::Move($source, $destination)
} else {
Write-Error "REFUSED automatic backup repair for $skill" -ErrorAction Continue
}
}
}
throw
} finally {
if ($null -ne $activeBackup -and (Test-Path -LiteralPath $activeBackup -PathType Container)) {
try { Remove-Item -LiteralPath $activeBackup -ErrorAction Stop } catch { }
}
if ($null -ne $lockDir -and (Test-Path -LiteralPath $lockDir -PathType Container)) {
Remove-Item -LiteralPath $lockDir
}
}Set the repository root explicitly for repository scope.
(
set -euo pipefail
bash /tmp/structured-thinking-preview-lifecycle.sh inspect user
REPO_ROOT="/absolute/path/to/target-repository"
bash /tmp/structured-thinking-preview-lifecycle.sh inspect repository "$REPO_ROOT"
)Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
pwsh -NoProfile -File "$env:TEMP/structured-thinking-preview-lifecycle.ps1" -Action inspect -Scope user
if ($LASTEXITCODE -ne 0) { throw "preview inspection failed" }
$REPO_ROOT = "C:\absolute\path\to\target-repository"
pwsh -NoProfile -File "$env:TEMP/structured-thinking-preview-lifecycle.ps1" `
-Action inspect -Scope repository -RepoRoot $REPO_ROOT
if ($LASTEXITCODE -ne 0) { throw "preview inspection failed" }Keep the old and new version's installer and lifecycle scripts under different, version-labelled filenames. A script validates only the version embedded in its own artifact; overwriting the old script would remove the safe rollback path.
- Clone the new source into a separate source directory. Save its installer and lifecycle script
separately; the installer will require its
skills/tree to be clean and committed. - Close sessions that loaded the current preview.
- Use the old version's lifecycle script to run
uninstallbelow, and record the backup path printed on standard output. - Run the new version's canonical installer in Installation against the new source and the same explicit scope.
- Start a fresh session and run all Quick Start smoke checks.
- If installation or smoke checks fail, use the new version's lifecycle script to uninstall the new copy, then use the old version's script to restore the recorded old backup. Never merge old and new directories.
The backup and installer staging directories are siblings of skills/, never children of the
discovery root.
The command first validates all three receipts and then moves all three directories out of Skill discovery. It preserves every unrelated Skill and leaves the shared root intact. Retain the printed backup path until the uninstall or upgrade has been accepted.
(
set -euo pipefail
BACKUP_ROOT="$(bash /tmp/structured-thinking-preview-lifecycle.sh uninstall user)"
printf 'rollback backup: %s\n' "$BACKUP_ROOT"
# Repository scope:
# REPO_ROOT="/absolute/path/to/target-repository"
# BACKUP_ROOT="$(bash /tmp/structured-thinking-preview-lifecycle.sh uninstall repository "$REPO_ROOT")"
)Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$BACKUP_ROOT = pwsh -NoProfile -File "$env:TEMP/structured-thinking-preview-lifecycle.ps1" `
-Action uninstall -Scope user
if ($LASTEXITCODE -ne 0) { throw "preview uninstall failed" }
Write-Host "rollback backup: $BACKUP_ROOT"
# Repository scope:
# $REPO_ROOT = "C:\absolute\path\to\target-repository"
# $BACKUP_ROOT = pwsh -NoProfile -File "$env:TEMP/structured-thinking-preview-lifecycle.ps1" `
# -Action uninstall -Scope repository -RepoRoot $REPO_ROOTStart a fresh session and confirm the three names are absent. The backup is not a discovery root and must not be added to one.
Restore requires all three selected destinations to be absent. If a newer preview is present, receipt-check and uninstall it first, retaining its separate backup.
(
set -euo pipefail
BACKUP_ROOT="/absolute/path/printed/by/uninstall"
bash /tmp/structured-thinking-preview-lifecycle.sh restore user "" "$BACKUP_ROOT"
# Repository scope:
# REPO_ROOT="/absolute/path/to/target-repository"
# bash /tmp/structured-thinking-preview-lifecycle.sh restore repository "$REPO_ROOT" "$BACKUP_ROOT"
)Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$BACKUP_ROOT = "C:\absolute\path\printed\by\uninstall"
pwsh -NoProfile -File "$env:TEMP/structured-thinking-preview-lifecycle.ps1" `
-Action restore -Scope user -BackupRoot $BACKUP_ROOT
if ($LASTEXITCODE -ne 0) { throw "preview restore failed" }
# Repository scope:
# $REPO_ROOT = "C:\absolute\path\to\target-repository"
# pwsh -NoProfile -File "$env:TEMP/structured-thinking-preview-lifecycle.ps1" `
# -Action restore -Scope repository -RepoRoot $REPO_ROOT -BackupRoot $BACKUP_ROOTStart a fresh session and repeat the smoke checks after restoration.
purge is irreversible. Use the same version-labelled lifecycle script that created the backup.
It accepts only a direct child of the controlled backup base, validates all three receipts, and
refuses a backup with missing or extra top-level entries.
(
set -euo pipefail
BACKUP_ROOT="/absolute/path/printed/by/uninstall"
bash /tmp/structured-thinking-preview-lifecycle.sh purge user "" "$BACKUP_ROOT"
# Repository scope:
# REPO_ROOT="/absolute/path/to/target-repository"
# bash /tmp/structured-thinking-preview-lifecycle.sh purge repository "$REPO_ROOT" "$BACKUP_ROOT"
)Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$BACKUP_ROOT = "C:\absolute\path\printed\by\uninstall"
pwsh -NoProfile -File "$env:TEMP/structured-thinking-preview-lifecycle.ps1" `
-Action purge -Scope user -BackupRoot $BACKUP_ROOT
if ($LASTEXITCODE -ne 0) { throw "preview purge failed" }
# Repository scope:
# $REPO_ROOT = "C:\absolute\path\to\target-repository"
# pwsh -NoProfile -File "$env:TEMP/structured-thinking-preview-lifecycle.ps1" `
# -Action purge -Scope repository -RepoRoot $REPO_ROOT -BackupRoot $BACKUP_ROOTA force-killed process may leave .structured-thinking-install.lock, a backup, or a partially
moved set. Do not remove the lock or move a directory merely to make the next command run. Confirm
that no installer or lifecycle process is active; inspect all three discovery destinations, the
candidate backup, and every receipt. A backup must remain directly under the documented backup
base, and its receipts must still name the original absolute destinations. If ownership or the
three-directory state is inconsistent, stop and preserve all artifacts for manual review. Remove an
identified stale lock only after the state is understood; then use restore or restart the complete
transaction instead of merging directories.
Only use /plugins, Settings → Plugins, or another host-managed lifecycle control when the
trusted listing is visible. Confirm the expected publisher and version before enable, update,
disable, uninstall, or reinstall. Do not manually edit host-managed Plugin files and do not use a
downloaded archive as a direct Plugin install. If an earlier trusted version is unavailable through
the host, disable the affected Plugin and follow authenticated release or revocation guidance.
This verifies a future release archive for inspection; it does not install that archive as a Plugin. Set the published version explicitly and obtain the archive and checksum from the same authenticated release page.
(
set -euo pipefail
VERSION="0.1.0-rc.1"
sha256sum --check "structured-thinking-${VERSION}.tar.gz.sha256"
)(
set -euo pipefail
VERSION="0.1.0-rc.1"
shasum -a 256 -c "structured-thinking-${VERSION}.tar.gz.sha256"
)Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$VERSION = "0.1.0-rc.1"
$archive = "structured-thinking-$VERSION.tar.gz"
$expected = ((Get-Content "$archive.sha256" -Raw).Trim() -split '\s+')[0].ToLowerInvariant()
$actual = (Get-FileHash -Algorithm SHA256 $archive).Hash.ToLowerInvariant()
if ($actual -ne $expected) { throw "Checksum mismatch; stop installation" }A mismatch stops extraction. Checksum equality detects corruption or substitution relative to the checksum file; it does not authenticate the publisher or release page.