========== BEGIN FILE ==========
---
label: '.\scripts\build_install.ps1 :: scripts/build_install.ps1'
path: scripts/build_install.ps1
glob: .\scripts\build_install.ps1
kind: glob
content_format: fenced
fence: '````'
---
````
rm dist
uv build
# find whl file in dist folder
$whl = Get-ChildItem -Path dist -Filter *.whl | Select-Object -First 1
pipx install $whl --force
````
========== END FILE ==========

========== BEGIN FILE ==========
---
label: .\scripts\debug_runner.py
path: scripts/debug_runner.py
kind: file
content_format: fenced
fence: '````'
---
````
#!/usr/bin/env python3
"""
Debug runner for sspec CLI commands.

This script allows debuggers (VS Code) to run sspec commands with breakpoint support.
It serves as the entry point for debugging sspec functionality.

Usage (direct CLI):
    python scripts/debug_runner.py change new
    python scripts/debug_runner.py ask create test --cwd /tmp/test-project
    python scripts/debug_runner.py project status --cwd .

Usage (VS Code prompt - shell-style):
    When prompted in VS Code, enter full command:
    change new --from test-a
    ask create test
    project status

Environment variables:
    DEBUG_CWD: Override working directory (same as --cwd)
"""

import os
import shlex
import sys
from pathlib import Path

# Add src/ to path so sspec module can be imported
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root / "src"))

from sspec.cli import main


def run() -> None:
    """
    Run sspec with the provided command-line arguments.

    Handles:
    - Shell-style command parsing (supports quoted args with spaces)
    - Converting --cwd to working directory change
    - Delegating to sspec.cli.main()
    """
    # Check for --cwd argument
    cwd = os.environ.get("DEBUG_CWD")

    argv = sys.argv[1:]

    # If single argument that looks like shell-style command, parse it
    # (happens when user enters full command in VS Code prompt)
    if len(argv) == 1 and ' ' in argv[0]:
        argv = shlex.split(argv[0])

    # Parse --cwd from argv if present
    if "--cwd" in argv:
        cwd_idx = argv.index("--cwd")
        if cwd_idx + 1 < len(argv):
            cwd = argv[cwd_idx + 1]
            # Remove --cwd and its value from argv
            argv = argv[:cwd_idx] + argv[cwd_idx + 2:]

    # Change directory if specified
    if cwd:
        os.chdir(cwd)
        print(f"📍 Working directory: {os.getcwd()}", file=sys.stderr)

    # Restore modified argv for Click to parse
    sys.argv = ["sspec"] + argv

    # Call sspec CLI
    main()


if __name__ == "__main__":
    run()
````
========== END FILE ==========

========== BEGIN FILE ==========
---
label: .\scripts\test-replace-link.ps1
path: scripts/test-replace-link.ps1
kind: file
content_format: fenced
fence: '````'
---
````
param(
    [switch]$AllowArchiveStale
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

function Invoke-UvInProject {
    param(
        [Parameter(Mandatory = $true)]
        [string]$ProjectDir,
        [Parameter(Mandatory = $true)]
        [string[]]$Args
    )

    Push-Location $ProjectDir
    try {
        Write-Host "`n> uv run $($Args -join ' ')" -ForegroundColor Cyan
        & uv run @Args
        if ($LASTEXITCODE -ne 0) {
            throw "Command failed: uv run $($Args -join ' ')"
        }
    }
    finally {
        Pop-Location
    }
}

function Assert-PathContains {
    param(
        [Parameter(Mandatory = $true)]
        [string]$FilePath,
        [Parameter(Mandatory = $true)]
        [string]$Needle
    )

    $content = Get-Content -Path $FilePath -Raw -Encoding UTF8
    if (-not $content.Contains($Needle)) {
        throw "Expected '$Needle' in $FilePath"
    }
}

function Find-SingleMatch {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Glob
    )

    $matches = @(Get-ChildItem -Path $Glob -File)
    if ($matches.Count -eq 0) {
        throw "No match found: $Glob"
    }

    if ($matches.Count -gt 1) {
        $list = $matches | ForEach-Object { $_.FullName }
        throw "Multiple matches for '$Glob':`n$($list -join "`n")"
    }

    return $matches[0]
}

$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
$projectDir = Join-Path $repoRoot 'tmp/test-replace-archive-link-project'
$sspecDir = Join-Path $projectDir '.sspec'

if (-not (Test-Path $projectDir)) {
    New-Item -ItemType Directory -Path $projectDir -Force | Out-Null
}

if (-not (Test-Path $sspecDir)) {
    Invoke-UvInProject -ProjectDir $projectDir -Args @('python', '-m', 'sspec.cli', 'project', 'init')
}

$suffix = (Get-Date -Format 'yyMMddHHmmss')
$rand = ([Guid]::NewGuid().ToString('N')).Substring(0, 6)

$requestName = "replace-link-$suffix-$rand"
$askName = "replace_ask_$suffix$rand"

Invoke-UvInProject -ProjectDir $projectDir -Args @('python', '-m', 'sspec.cli', 'request', 'new', $requestName)
Invoke-UvInProject -ProjectDir $projectDir -Args @('python', '-m', 'sspec.cli', 'change', 'new', '--from', $requestName)

$requestFile = Find-SingleMatch -Glob (Join-Path $sspecDir "requests/*_${requestName}.md")

$changeDirCandidates = @(Get-ChildItem -Path (Join-Path $sspecDir 'changes') -Directory |
    Where-Object { $_.Name -ne 'archive' -and $_.Name -like "*_${requestName}" })

if ($changeDirCandidates.Count -eq 0) {
    throw "Cannot locate change dir for request '$requestName'"
}

$changeDir = $changeDirCandidates | Sort-Object LastWriteTime -Descending | Select-Object -First 1
$changeSpec = Join-Path $changeDir.FullName 'spec.md'
$changeHandover = Join-Path $changeDir.FullName 'handover.md'
$changeRefDir = Join-Path $changeDir.FullName 'reference'
$changeRefFile = Join-Path $changeRefDir 'link-check.md'

$askFileName = "$askName.md"
$askPath = Join-Path $sspecDir "asks/$askFileName"

$requestRel = ".sspec/requests/$($requestFile.Name)"
$changeDirRel = ".sspec/changes/$($changeDir.Name)"
$changeSpecRel = "$changeDirRel/spec.md"
$changeHandoverRel = "$changeDirRel/handover.md"
$askRel = ".sspec/asks/$askFileName"
$tmpRel = '.sspec/tmp/link-check.md'

$askContent = @"
---
created: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')
name: $askName
why: test archive link replacement
---

Ask references:
- $requestRel
- $changeSpecRel
- $changeHandoverRel
- $tmpRel
"@
Set-Content -Path $askPath -Value $askContent -Encoding UTF8

Add-Content -Path $requestFile.FullName -Value @"

## Link Replacement Test
- ask: $askRel
- change-spec: $changeSpecRel
- change-handover: $changeHandoverRel
"@ -Encoding UTF8

Add-Content -Path $changeSpec -Value @"

## Link Replacement Test
- request: $requestRel
- ask: $askRel
- request-again: $requestRel
"@ -Encoding UTF8

Add-Content -Path $changeHandover -Value @"

## Link Replacement Test
- request: $requestRel
- ask: $askRel
"@ -Encoding UTF8

Set-Content -Path $changeRefFile -Value @"
# Link Test

- request: $requestRel
- ask: $askRel
- spec: $changeSpecRel
"@ -Encoding UTF8

$tmpDir = Join-Path $sspecDir 'tmp'
if (-not (Test-Path $tmpDir)) {
    New-Item -ItemType Directory -Path $tmpDir -Force | Out-Null
}

$tmpFile = Join-Path $tmpDir 'link-check.md'
Set-Content -Path $tmpFile -Value @"
# tmp link holder

- request: $requestRel
- ask: $askRel
- change-spec: $changeSpecRel
"@ -Encoding UTF8

Write-Host 'Initial setup complete. Starting archive process...' -ForegroundColor Yellow
# Pause

Invoke-UvInProject -ProjectDir $projectDir -Args @('python', '-m', 'sspec.cli', 'request', 'archive', $requestName, '-y')
Invoke-UvInProject -ProjectDir $projectDir -Args @('python', '-m', 'sspec.cli', 'change', 'archive', $requestName, '--yes')
Invoke-UvInProject -ProjectDir $projectDir -Args @('python', '-m', 'sspec.cli', 'ask', 'archive', $askName, '-y')

$requestArchivedRel = ".sspec/requests/archive/$($requestFile.Name)"
$changeArchivedDirRel = ".sspec/changes/archive/$($changeDir.Name)"
$askArchivedRel = ".sspec/asks/archive/$askFileName"

$checkDirs = @(
    (Join-Path $sspecDir 'requests'),
    (Join-Path $sspecDir 'changes'),
    (Join-Path $sspecDir 'asks'),
    (Join-Path $sspecDir 'tmp')
)

$mdFiles = @()
foreach ($dir in $checkDirs) {
    if (Test-Path $dir) {
        $mdFiles += Get-ChildItem -Path $dir -Filter '*.md' -Recurse -File
    }
}

foreach ($md in $mdFiles) {
    if ($md.FullName -like '*\archive\*') {
        continue
    }

    $content = Get-Content -Path $md.FullName -Raw -Encoding UTF8
    if ($content.Contains($requestRel)) {
        throw "Old request path still exists in: $($md.FullName)"
    }
    if ($content.Contains($changeDirRel)) {
        throw "Old change path still exists in: $($md.FullName)"
    }
    if ($content.Contains($askRel)) {
        throw "Old ask path still exists in: $($md.FullName)"
    }
}

if (-not $AllowArchiveStale) {
    $archiveFiles = $mdFiles | Where-Object { $_.FullName -like '*\archive\*' }
    foreach ($md in $archiveFiles) {
        $content = Get-Content -Path $md.FullName -Raw -Encoding UTF8
        if ($content.Contains($requestRel)) {
            throw "Archive file still contains old request path: $($md.FullName)"
        }
        if ($content.Contains($changeDirRel)) {
            throw "Archive file still contains old change path: $($md.FullName)"
        }
        if ($content.Contains($askRel)) {
            throw "Archive file still contains old ask path: $($md.FullName)"
        }
    }
}

Assert-PathContains -FilePath $tmpFile -Needle $requestArchivedRel
Assert-PathContains -FilePath $tmpFile -Needle $changeArchivedDirRel
Assert-PathContains -FilePath $tmpFile -Needle $askArchivedRel

Write-Host ''
Write-Host 'PASS: archive link replacement smoke test completed.' -ForegroundColor Green
Write-Host "Project: $projectDir" -ForegroundColor DarkGray
Write-Host "Request: $requestArchivedRel" -ForegroundColor DarkGray
Write-Host "Change : $changeArchivedDirRel" -ForegroundColor DarkGray
Write-Host "Ask    : $askArchivedRel" -ForegroundColor DarkGray
if ($AllowArchiveStale) {
    Write-Host 'Mode   : archive stale links are tolerated (AllowArchiveStale)' -ForegroundColor DarkGray
}
````
========== END FILE ==========

========== BEGIN FILE ==========
---
label: .\scripts\uv_publish.ps1
path: scripts/uv_publish.ps1
kind: file
content_format: fenced
fence: '````'
---
````
param(
    [Parameter(ValueFromRemainingArguments = $true)]
    [string[]]$PublishArgs
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

function Get-UvToken {
    param(
        [string[]]$Hosts
    )

    foreach ($serviceHost in $Hosts) {
        try {
            $candidate = uv auth token $serviceHost 2>$null
            if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace($candidate)) {
                return $candidate.Trim()
            }
        }
        catch {
            # Try next host.
        }
    }

    return $null
}

$token = Get-UvToken -Hosts @("upload.pypi.org", "pypi.org")
if ([string]::IsNullOrWhiteSpace($token)) {
    Write-Error "No PyPI token found in uv auth store. Run: uv auth login upload.pypi.org --token <pypi-token>"
    exit 1
}

$hadPrevious = $false
$previous = [Environment]::GetEnvironmentVariable("UV_PUBLISH_TOKEN", "Process")
if ($null -ne $previous) {
    $hadPrevious = $true
}

try {
    $env:UV_PUBLISH_TOKEN = $token
    uv publish @PublishArgs
    exit $LASTEXITCODE
}
finally {
    if ($hadPrevious) {
        $env:UV_PUBLISH_TOKEN = $previous
    }
    else {
        Remove-Item Env:UV_PUBLISH_TOKEN -ErrorAction SilentlyContinue
    }
}
````
========== END FILE ==========
