From 2aabcb4fbae417261e7556f8112605c41d9b8fb6 Mon Sep 17 00:00:00 2001 From: alexey1312 Date: Tue, 24 Feb 2026 09:30:26 +0500 Subject: [PATCH 1/2] feat: add Windows CI support with Swift 6.3 and swift-jinja migration - Add Windows build to ci.yml and release.yml using Swift 6.3 - Replace Stencil with swift-jinja for cross-platform compatibility - Add JinjaSupport shared module for template rendering - Make XcodeProj conditional (#if !os(Windows)) in Package.swift - Wrap XcodeProjectWriter in #if canImport(XcodeProj) (6 export files) - Use #if canImport(FoundationNetworking) instead of #if os(Linux) - Pin swift-resvg to 0.45.1-swift.15 (Windows artifactbundle) - Delete standalone windows-test.yml - Update docs: CLAUDE.md, linux-compat.md, README.md Co-Authored-By: Claude Opus 4.6 --- .claude/rules/linux-compat.md | 64 +++++++- .github/workflows/ci.yml | 25 +++ .github/workflows/release.yml | 41 ++++- .github/workflows/windows-test.yml | 36 ----- CLAUDE.md | 59 +++---- Package.resolved | 6 +- Package.swift | 95 +++++++----- README.md | 4 +- .../ExFigCLI/ExFig.docc/CustomTemplates.md | 1 + Sources/ExFigCLI/Output/FileDownloader.swift | 2 +- Sources/ExFigCLI/Output/FileWriter.swift | 2 +- .../ExFigCLI/Output/XcodeProjectWriter.swift | 146 +++++++++--------- .../Pipeline/SharedDownloadQueue.swift | 2 +- .../Export/PluginColorsExport.swift | 36 +++-- .../Export/PluginIconsExport.swift | 34 ++-- .../Export/PluginImagesExport.swift | 34 ++-- .../Export/PluginTypographyExport.swift | 40 ++--- .../Subcommands/Export/iOSColorsExport.swift | 26 ++-- Sources/FigmaAPI/Client.swift | 2 +- Sources/FigmaAPI/Endpoint/BaseEndpoint.swift | 2 +- .../Endpoint/ComponentsEndpoint.swift | 2 +- Sources/FigmaAPI/Endpoint/Endpoint.swift | 2 +- .../Endpoint/FileMetadataEndpoint.swift | 2 +- Sources/FigmaAPI/Endpoint/ImageEndpoint.swift | 2 +- .../Endpoint/LatestReleaseEndpoint.swift | 2 +- Sources/FigmaAPI/Endpoint/NodesEndpoint.swift | 2 +- .../FigmaAPI/Endpoint/StylesEndpoint.swift | 2 +- .../Endpoint/UpdateVariablesEndpoint.swift | 2 +- .../FigmaAPI/Endpoint/VariablesEndpoint.swift | 2 +- Sources/FigmaAPI/FigmaAPIError.swift | 2 +- Sources/FigmaAPI/FigmaClient.swift | 2 +- Sources/FigmaAPI/GitHubClient.swift | 2 +- Sources/FigmaAPI/RateLimitedClient.swift | 2 +- Sources/FigmaAPI/RetryPolicy.swift | 2 +- Sources/SVGKit/SVGParser.swift | 2 +- 35 files changed, 397 insertions(+), 290 deletions(-) delete mode 100644 .github/workflows/windows-test.yml diff --git a/.claude/rules/linux-compat.md b/.claude/rules/linux-compat.md index b2f9682c..1d8eb8ab 100644 --- a/.claude/rules/linux-compat.md +++ b/.claude/rules/linux-compat.md @@ -1,8 +1,8 @@ -# Linux Compatibility +# Linux & Windows Compatibility -This rule covers Linux-specific workarounds and differences from macOS. +This rule covers Linux- and Windows-specific workarounds and differences from macOS. -The project builds on Linux (Ubuntu 22.04 LTS / Jammy). Key differences from macOS: +The project builds on Linux (Ubuntu 22.04 LTS / Jammy) and Windows (Swift 6.3). Key differences from macOS: ## Required Import for Networking @@ -44,8 +44,56 @@ func testSomePngOperation() throws { ## Platform-Specific Features -| Feature | macOS | Linux | -| ------------ | ------------ | ------------------------ | -| HEIC encoding| ImageIO | Falls back to PNG | -| libpng tests | Full support | Build tests first | -| Foundation | Full | Some APIs missing/broken | +| Feature | macOS | Linux | Windows | +| ------------ | ------------ | ------------------------ | ------------------------ | +| HEIC encoding| ImageIO | Falls back to PNG | Falls back to PNG | +| libpng tests | Full support | Build tests first | Not tested | +| Foundation | Full | Some APIs missing/broken | Some APIs missing/broken | +| XcodeProj | Full | Full | Not available | +| Swift version| 6.2+ | 6.2+ | 6.3 required | + +## Windows Support + +### Swift Version + +Windows requires Swift 6.3 (development snapshot) due to `swift-resvg` artifactbundle compatibility. +CI uses `compnerd/gha-setup-swift@v0.3.0` with `swift-6.3-branch`. + +### Conditional Dependencies (Package.swift) + +`#if` inside array literals does NOT work in SPM Package.swift. Use variable + `#if` append pattern: + +```swift +var packageDependencies: [Package.Dependency] = [...] +#if !os(Windows) + packageDependencies.append(.package(url: "https://github.com/tuist/XcodeProj.git", from: "8.27.0")) +#endif +``` + +### XcodeProj Exclusion + +XcodeProj is Apple-only (depends on PathKit/AEXML). On Windows: +- Dependency excluded via `#if !os(Windows)` in Package.swift +- `XcodeProjectWriter` wrapped in `#if canImport(XcodeProj)` (6 files in Export/) +- Xcode project manipulation silently skipped on Windows + +### FoundationNetworking / FoundationXML + +Use `#if canImport()` instead of `#if os(Linux)` — covers both Linux and Windows: + +```swift +#if canImport(FoundationNetworking) + import FoundationNetworking +#endif + +#if canImport(FoundationXML) + import FoundationXML +#endif +``` + +### SPM Artifactbundle on Windows + +SPM on Windows has library naming differences: +- Unix linkers auto-prepend `lib` prefix (`-lresvg` finds `libresvg.a`) +- Windows `lld-link` does NOT prepend prefix (`resvg.lib` must exist as-is) +- Swift 6.3 allows `.lib` files without `lib` prefix in artifactbundle info.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e716ea2b..a1656693 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,3 +125,28 @@ jobs: - name: Run tests # Build tests separately to avoid hangs from concurrent build + test execution run: swift test --skip-build --parallel 2>&1 | xcsift + + build-windows: + name: Build (Windows) + runs-on: windows-latest + needs: lint + steps: + - uses: actions/checkout@v6 + + - name: Install Swift + uses: compnerd/gha-setup-swift@v0.3.0 + with: + branch: swift-6.3-branch + tag: 6.3-DEVELOPMENT-SNAPSHOT-2026-02-21-a + + - name: Swift version + run: swift --version + + - name: Resolve dependencies + run: swift package resolve + + - name: Build (Debug) + run: swift build + + - name: Build (Release) + run: swift build -c release diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7279f0ff..64bd3e78 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -28,6 +28,10 @@ jobs: archive-name: exfig-linux-x64 container: swift:6.2-jammy extra-flags: --static-swift-stdlib -Xlinker -lcurl -Xlinker -lxml2 -Xlinker -lssl -Xlinker -lcrypto -Xlinker -lz + - os: windows-latest + platform: windows-x64 + build-path: x86_64-unknown-windows-msvc/release + archive-name: exfig-windows-x64 runs-on: ${{ matrix.os }} @@ -45,6 +49,13 @@ jobs: with: lfs: true + - name: Install Swift (Windows) + if: matrix.platform == 'windows-x64' + uses: compnerd/gha-setup-swift@v0.3.0 + with: + branch: swift-6.3-branch + tag: 6.3-DEVELOPMENT-SNAPSHOT-2026-02-21-a + - name: Select Xcode 26.1 (macOS) if: matrix.platform == 'macos' run: sudo xcode-select -s /Applications/Xcode_26.1.1.app/Contents/Developer @@ -54,7 +65,8 @@ jobs: run: | apt-get install -y libcurl4-openssl-dev libxml2-dev libssl-dev - - name: Set version from tag + - name: Set version from tag (Unix) + if: matrix.platform != 'windows-x64' run: | VERSION="${GITHUB_REF#refs/tags/}" FILE="Sources/ExFigCLI/ExFigCommand.swift" @@ -64,6 +76,14 @@ jobs: sed -i "s/static let version = \".*\"/static let version = \"$VERSION\"/" "$FILE" fi + - name: Set version from tag (Windows) + if: matrix.platform == 'windows-x64' + shell: pwsh + run: | + $version = $env:GITHUB_REF -replace '^refs/tags/', '' + $file = "Sources/ExFigCLI/ExFigCommand.swift" + (Get-Content $file) -replace 'static let version = ".*"', "static let version = `"$version`"" | Set-Content $file + - name: Build release binary (macOS) if: matrix.platform == 'macos' run: | @@ -83,6 +103,10 @@ jobs: if: matrix.platform == 'linux-x64' run: swift build -c release ${{ matrix.extra-flags }} + - name: Build release binary (Windows) + if: matrix.platform == 'windows-x64' + run: swift build -c release + - name: Create release archive (macOS) if: matrix.platform == 'macos' run: | @@ -109,6 +133,20 @@ jobs: cp LICENSE dist/ cd dist && tar -czf ../${{ matrix.archive-name }}.tar.gz -- * + - name: Create release archive (Windows) + if: matrix.platform == 'windows-x64' + shell: pwsh + run: | + New-Item -ItemType Directory -Force -Path dist + Copy-Item ".build/${{ matrix.build-path }}/exfig.exe" "dist/ExFig.exe" + Copy-Item ".build/${{ matrix.build-path }}/exfig_AndroidExport.resources" "dist/" -Recurse -ErrorAction SilentlyContinue + Copy-Item ".build/${{ matrix.build-path }}/exfig_XcodeExport.resources" "dist/" -Recurse -ErrorAction SilentlyContinue + Copy-Item ".build/${{ matrix.build-path }}/exfig_FlutterExport.resources" "dist/" -Recurse -ErrorAction SilentlyContinue + Copy-Item ".build/${{ matrix.build-path }}/exfig_WebExport.resources" "dist/" -Recurse -ErrorAction SilentlyContinue + Copy-Item ".build/${{ matrix.build-path }}/exfig_ExFigCLI.resources" "dist/" -Recurse -ErrorAction SilentlyContinue + Copy-Item "LICENSE" "dist/" + Compress-Archive -Path "dist/*" -DestinationPath "${{ matrix.archive-name }}.zip" + - name: Upload artifact uses: actions/upload-artifact@v4 with: @@ -200,6 +238,7 @@ jobs: files: | artifacts/exfig-macos/exfig-macos.zip artifacts/exfig-linux-x64/exfig-linux-x64.tar.gz + artifacts/exfig-windows-x64/exfig-windows-x64.zip .pkl-out/exfig@*/* update-homebrew: diff --git a/.github/workflows/windows-test.yml b/.github/workflows/windows-test.yml deleted file mode 100644 index 647126f4..00000000 --- a/.github/workflows/windows-test.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: Windows Build Test - -on: - workflow_dispatch: - -jobs: - build-windows: - runs-on: windows-latest - steps: - - uses: actions/checkout@v4 - - # Swift 6.0.x is incompatible with Windows 11 SDK 26100 - # See: https://github.com/swiftlang/swift/issues/79745 - - name: Downgrade Windows SDK for Swift 6 compatibility - run: | - & "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe" modify ` - --remove Microsoft.VisualStudio.Component.Windows11SDK.26100 ` - --installPath "C:\Program Files\Microsoft Visual Studio\2022\Enterprise" ` - --quiet --norestart - - # Note: Swift 6.2 for Windows may not be available yet - # Check https://www.swift.org/download/ for latest Windows releases - - name: Install Swift - uses: compnerd/gha-setup-swift@main - with: - branch: swift-6.2-release - tag: 6.2-RELEASE - - - name: Swift version - run: swift --version - - - name: Build (Debug) - run: swift build - - - name: Build (Release) - run: swift build -c release diff --git a/CLAUDE.md b/CLAUDE.md index 7d9e9d9e..2b9c98b5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -106,21 +106,21 @@ pkl eval --format json # Package URI requires published package ## Project Context -| Aspect | Details | -| --------------- | ---------------------------------------------------------------------------------- | -| Language | Swift 6.2, macOS 13.0+ | -| Package Manager | Swift Package Manager | -| CLI Framework | swift-argument-parser | -| Config Format | PKL (Programmable, Scalable, Safe) | -| Templates | Jinja2 (swift-jinja) | -| Required Env | `FIGMA_PERSONAL_TOKEN` | -| Config Files | `exfig.pkl` (PKL configuration) | -| Tooling | mise (`./bin/mise` self-contained, no global install needed) | -| Platforms | macOS 13+ (primary), Linux/Ubuntu 22.04 (CI) - see `.claude/rules/linux-compat.md` | +| Aspect | Details | +| --------------- | -------------------------------------------------------------------------------------------------- | +| Language | Swift 6.2, macOS 13.0+ | +| Package Manager | Swift Package Manager | +| CLI Framework | swift-argument-parser | +| Config Format | PKL (Programmable, Scalable, Safe) | +| Templates | Jinja2 (swift-jinja) | +| Required Env | `FIGMA_PERSONAL_TOKEN` | +| Config Files | `exfig.pkl` (PKL configuration) | +| Tooling | mise (`./bin/mise` self-contained, no global install needed) | +| Platforms | macOS 13+ (primary), Linux/Ubuntu 22.04, Windows (Swift 6.3) - see `.claude/rules/linux-compat.md` | ## Architecture -Twelve modules in `Sources/`: +Thirteen modules in `Sources/`: | Module | Purpose | | --------------- | --------------------------------------------------------- | @@ -137,6 +137,7 @@ Twelve modules in `Sources/`: | `FlutterExport` | Flutter export (Dart code, SVG/PNG assets) | | `WebExport` | Web/React export (CSS variables, JSX icons) | | `SVGKit` | SVG parsing, ImageVector/VectorDrawable generation | +| `JinjaSupport` | Shared Jinja2 template rendering helpers | **Data flow:** CLI -> PKL config parsing -> FigmaAPI fetch -> ExFigCore processing -> Platform plugin -> Export module -> File write @@ -326,22 +327,22 @@ NooraUI.formatLink("url", useColors: true) // underlined primary ## Dependencies -| Package | Version | Purpose | -| --------------------- | ------- | ------------------------------- | -| swift-argument-parser | 1.5.0+ | CLI framework | -| swift-collections | 1.2.x | Ordered collections | -| swift-jinja | 2.0.0+ | Jinja2 template engine | -| XcodeProj | 8.27.0+ | Xcode project manipulation | -| swift-log | 1.6.0+ | Logging | -| Rainbow | 4.2.0+ | Terminal colors | -| libwebp | 1.4.1+ | WebP encoding | -| libpng | 1.6.45+ | PNG decoding | -| swift-custom-dump | 1.3.0+ | Test assertions | -| Noora | 0.54.0+ | Terminal UI design system | -| swift-resvg | 0.45.1 | SVG parsing/rendering | -| swift-docc-plugin | 1.4.5+ | DocC documentation | -| swift-yyjson | 0.5.0+ | High-performance JSON codec | -| pkl-swift | 0.7.2+ | PKL config evaluation & codegen | +| Package | Version | Purpose | +| --------------------- | --------------- | -------------------------------------------------- | +| swift-argument-parser | 1.5.0+ | CLI framework | +| swift-collections | 1.2.x | Ordered collections | +| swift-jinja | 2.0.0+ | Jinja2 template engine | +| XcodeProj | 8.27.0+ | Xcode project manipulation (macOS/Linux only) | +| swift-log | 1.6.0+ | Logging | +| Rainbow | 4.2.0+ | Terminal colors | +| libwebp | 1.4.1+ | WebP encoding | +| libpng | 1.6.45+ | PNG decoding | +| swift-custom-dump | 1.3.0+ | Test assertions | +| Noora | 0.54.0+ | Terminal UI design system | +| swift-resvg | 0.45.1-swift.15 | SVG parsing/rendering (Windows requires Swift 6.3) | +| swift-docc-plugin | 1.4.5+ | DocC documentation | +| swift-yyjson | 0.5.0+ | High-performance JSON codec | +| pkl-swift | 0.7.2+ | PKL config evaluation & codegen | ## Troubleshooting @@ -379,7 +380,7 @@ Contextual documentation is in `.claude/rules/`: | `cache-granular.md` | Experimental granular node-level cache | | `api-reference.md` | Figma API endpoints, response mapping | | `gotchas.md` | Swift 6 concurrency, SwiftLint, rate limits | -| `linux-compat.md` | Linux-specific workarounds | +| `linux-compat.md` | Linux/Windows platform workarounds | | `testing-workflow.md` | Testing guidelines, commit format | | `pkl-codegen.md` | pkl-swift generated types, enum bridging, codegen | | `Sources/*/CLAUDE.md` | Module-specific patterns, modification checklists | diff --git a/Package.resolved b/Package.resolved index 4ff594f8..0bff6640 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,5 +1,5 @@ { - "originHash" : "011f6ae74b685e4f02cbafb2b52fa7c4999ec379a804ef0dd84111453b28e959", + "originHash" : "6f01253201cc38de3e263d63a2f79d69a6d990126959e4b9aa5db7ebe2cb4159", "pins" : [ { "identity" : "aexml", @@ -159,8 +159,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/alexey1312/swift-resvg.git", "state" : { - "revision" : "f408b4a39dd59b402f1db3605daff6748c5e81ec", - "version" : "0.45.1-swift.3" + "revision" : "6707e4f94a05b4ea91e82cafa9b6c4ecb78a73bb", + "version" : "0.45.1-swift.15" } }, { diff --git a/Package.swift b/Package.swift index f8132ccd..c2d764e7 100644 --- a/Package.swift +++ b/Package.swift @@ -3,6 +3,58 @@ import PackageDescription +// MARK: - Conditional Dependencies + +var packageDependencies: [Package.Dependency] = [ + .package(url: "https://github.com/apple/swift-collections", "1.2.0" ..< "1.3.0"), + .package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"), + .package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"), + .package(url: "https://github.com/huggingface/swift-jinja.git", from: "2.0.0"), + .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.0"), + + .package(url: "https://github.com/the-swift-collective/libwebp.git", from: "1.4.1"), + .package(url: "https://github.com/the-swift-collective/libpng.git", from: "1.6.45"), + .package(url: "https://github.com/tuist/Noora", from: "0.54.0"), + .package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.4.5"), + .package(url: "https://github.com/alexey1312/swift-resvg.git", exact: "0.45.1-swift.15"), + .package(url: "https://github.com/mattt/swift-yyjson", from: "0.5.0"), + .package(url: "https://github.com/apple/pkl-swift", from: "0.7.2"), +] + +var exfigCLIDependencies: [Target.Dependency] = [ + "FigmaAPI", + "ExFigCore", + "ExFigConfig", + "XcodeExport", + "AndroidExport", + "FlutterExport", + "WebExport", + "SVGKit", + "ExFig-iOS", + "ExFig-Android", + "ExFig-Flutter", + "ExFig-Web", + .product(name: "Resvg", package: "swift-resvg"), + .product(name: "ArgumentParser", package: "swift-argument-parser"), + .product(name: "Logging", package: "swift-log"), + + .product(name: "WebP", package: "libwebp"), + .product(name: "LibPNG", package: "libpng"), + .product(name: "Noora", package: "Noora"), +] + +// XcodeProj is Apple-only (not available on Windows) +#if !os(Windows) + packageDependencies.append( + .package(url: "https://github.com/tuist/XcodeProj.git", from: "8.27.0") + ) + exfigCLIDependencies.append( + .product(name: "XcodeProj", package: "XcodeProj") + ) +#endif + +// MARK: - Package + let package = Package( name: "exfig", platforms: [ @@ -11,48 +63,12 @@ let package = Package( products: [ .executable(name: "exfig", targets: ["ExFigCLI"]), ], - dependencies: [ - .package(url: "https://github.com/apple/swift-collections", "1.2.0" ..< "1.3.0"), - .package(url: "https://github.com/apple/swift-argument-parser", from: "1.5.0"), - .package(url: "https://github.com/apple/swift-log.git", from: "1.6.0"), - .package(url: "https://github.com/huggingface/swift-jinja.git", from: "2.0.0"), - .package(url: "https://github.com/tuist/XcodeProj.git", from: "8.27.0"), - .package(url: "https://github.com/pointfreeco/swift-custom-dump", from: "1.3.0"), - - .package(url: "https://github.com/the-swift-collective/libwebp.git", from: "1.4.1"), - .package(url: "https://github.com/the-swift-collective/libpng.git", from: "1.6.45"), - .package(url: "https://github.com/tuist/Noora", from: "0.54.0"), - .package(url: "https://github.com/swiftlang/swift-docc-plugin", from: "1.4.5"), - .package(url: "https://github.com/alexey1312/swift-resvg.git", exact: "0.45.1-swift.3"), - .package(url: "https://github.com/mattt/swift-yyjson", from: "0.5.0"), - .package(url: "https://github.com/apple/pkl-swift", from: "0.7.2"), - ], + dependencies: packageDependencies, targets: [ // Main target .executableTarget( name: "ExFigCLI", - dependencies: [ - "FigmaAPI", - "ExFigCore", - "ExFigConfig", - "XcodeExport", - "AndroidExport", - "FlutterExport", - "WebExport", - "SVGKit", - "ExFig-iOS", - "ExFig-Android", - "ExFig-Flutter", - "ExFig-Web", - .product(name: "Resvg", package: "swift-resvg"), - .product(name: "XcodeProj", package: "XcodeProj"), - .product(name: "ArgumentParser", package: "swift-argument-parser"), - .product(name: "Logging", package: "swift-log"), - - .product(name: "WebP", package: "libwebp"), - .product(name: "LibPNG", package: "libpng"), - .product(name: "Noora", package: "Noora"), - ], + dependencies: exfigCLIDependencies, exclude: ["CLAUDE.md", "AGENTS.md"], resources: [ .copy("Resources/Schemas/"), @@ -92,7 +108,8 @@ let package = Package( name: "JinjaSupport", dependencies: [ .product(name: "Jinja", package: "swift-jinja"), - ] + ], + exclude: ["CLAUDE.md", "AGENTS.md"] ), // Exports resources to Xcode project diff --git a/README.md b/README.md index caa3b9bf..f5a17f56 100644 --- a/README.md +++ b/README.md @@ -257,8 +257,8 @@ Automate design exports in CI/CD with [exfig-action](https://github.com/alexey13 ## Requirements -- **Swift 6.2+** (for building from source) -- **macOS 13.0+** or **Linux (Ubuntu 22.04)** +- **Swift 6.2+** (macOS/Linux), **Swift 6.3+** (Windows) +- **macOS 13.0+**, **Linux (Ubuntu 22.04)**, or **Windows** - **[PKL](https://pkl-lang.org/)** - Configuration language (install via `brew install pkl` or `mise use -g pkl`) - **Figma Personal Access Token** diff --git a/Sources/ExFigCLI/ExFig.docc/CustomTemplates.md b/Sources/ExFigCLI/ExFig.docc/CustomTemplates.md index 05a4492b..9cf941b6 100644 --- a/Sources/ExFigCLI/ExFig.docc/CustomTemplates.md +++ b/Sources/ExFigCLI/ExFig.docc/CustomTemplates.md @@ -335,6 +335,7 @@ images: [{ componentName: "HeroBanner", fileName: "hero_banner" }] {% endif %} ``` + ## Jinja2 Syntax Reference ### Variables diff --git a/Sources/ExFigCLI/Output/FileDownloader.swift b/Sources/ExFigCLI/Output/FileDownloader.swift index 62a129e2..b4c36a39 100644 --- a/Sources/ExFigCLI/Output/FileDownloader.swift +++ b/Sources/ExFigCLI/Output/FileDownloader.swift @@ -1,7 +1,7 @@ import ExFigCore import Foundation import Logging -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/ExFigCLI/Output/FileWriter.swift b/Sources/ExFigCLI/Output/FileWriter.swift index fb0463aa..c1c93655 100644 --- a/Sources/ExFigCLI/Output/FileWriter.swift +++ b/Sources/ExFigCLI/Output/FileWriter.swift @@ -1,6 +1,6 @@ import ExFigCore import Foundation -#if os(Linux) +#if canImport(FoundationXML) import FoundationXML #endif diff --git a/Sources/ExFigCLI/Output/XcodeProjectWriter.swift b/Sources/ExFigCLI/Output/XcodeProjectWriter.swift index ade5abeb..fc7e3367 100644 --- a/Sources/ExFigCLI/Output/XcodeProjectWriter.swift +++ b/Sources/ExFigCLI/Output/XcodeProjectWriter.swift @@ -1,90 +1,92 @@ -import Foundation -import PathKit -import XcodeProj +#if canImport(XcodeProj) + import Foundation + import PathKit + import XcodeProj -enum XcodeProjectWriterError: LocalizedError { - case unableToFindTarget(String) + enum XcodeProjectWriterError: LocalizedError { + case unableToFindTarget(String) - var errorDescription: String? { - switch self { - case let .unableToFindTarget(name): - "Target not found: \(name)" + var errorDescription: String? { + switch self { + case let .unableToFindTarget(name): + "Target not found: \(name)" + } } - } - var recoverySuggestion: String? { - switch self { - case .unableToFindTarget: - "Check target name in Xcode project settings" + var recoverySuggestion: String? { + switch self { + case .unableToFindTarget: + "Check target name in Xcode project settings" + } } } -} -final class XcodeProjectWriter { - let xcodeprojPath: Path - let rootPath = Path("./") - let xcodeproj: XcodeProj - let pbxproj: PBXProj - let myTarget: PBXTarget - let project: PBXProject + final class XcodeProjectWriter { + let xcodeprojPath: Path + let rootPath = Path("./") + let xcodeproj: XcodeProj + let pbxproj: PBXProj + let myTarget: PBXTarget + let project: PBXProject - init(xcodeProjPath: String, target: String) throws { - xcodeprojPath = Path(xcodeProjPath) - xcodeproj = try XcodeProj(path: xcodeprojPath) - pbxproj = xcodeproj.pbxproj - if let target = pbxproj.targets(named: target).first { - myTarget = target - } else { - throw XcodeProjectWriterError.unableToFindTarget(target) - } - guard let firstProject = pbxproj.projects.first else { - throw XcodeProjectWriterError.unableToFindTarget("No project found") + init(xcodeProjPath: String, target: String) throws { + xcodeprojPath = Path(xcodeProjPath) + xcodeproj = try XcodeProj(path: xcodeprojPath) + pbxproj = xcodeproj.pbxproj + if let target = pbxproj.targets(named: target).first { + myTarget = target + } else { + throw XcodeProjectWriterError.unableToFindTarget(target) + } + guard let firstProject = pbxproj.projects.first else { + throw XcodeProjectWriterError.unableToFindTarget("No project found") + } + project = firstProject } - project = firstProject - } - func addFileReferenceToXcodeProj(_ url: URL) throws { - var groups = url.pathComponents - .filter { $0 != "." } - .dropLast() as Array + func addFileReferenceToXcodeProj(_ url: URL) throws { + var groups = url.pathComponents + .filter { $0 != "." } + .dropLast() as Array - var currentGroup: PBXGroup? = project.mainGroup - var prevGroup: PBXGroup? + var currentGroup: PBXGroup? = project.mainGroup + var prevGroup: PBXGroup? - while currentGroup != nil { - if groups.isEmpty { break } - let group = currentGroup?.children.first(where: { group -> Bool in - group.path == groups.first - }) - if let group { - prevGroup = currentGroup - currentGroup = group as? PBXGroup - groups = Array(groups.dropFirst()) - } else { - prevGroup = currentGroup - let groupName = groups[0] - currentGroup = try prevGroup?.addGroup(named: groupName).first - groups = Array(groups.dropFirst()) + while currentGroup != nil { + if groups.isEmpty { break } + let group = currentGroup?.children.first(where: { group -> Bool in + group.path == groups.first + }) + if let group { + prevGroup = currentGroup + currentGroup = group as? PBXGroup + groups = Array(groups.dropFirst()) + } else { + prevGroup = currentGroup + let groupName = groups[0] + currentGroup = try prevGroup?.addGroup(named: groupName).first + groups = Array(groups.dropFirst()) + } } - } - guard currentGroup?.children.first(where: { $0.path == url.lastPathComponent }) == nil else { return } + guard currentGroup?.children.first(where: { $0.path == url.lastPathComponent }) == nil else { return } - let newFile = try currentGroup?.addFile( - at: Path(url.path), - sourceTree: .group, - sourceRoot: rootPath, - override: false, - validatePresence: true - ) - newFile?.fileEncoding = 4 // UTF-8 - newFile?.name = url.lastPathComponent + let newFile = try currentGroup?.addFile( + at: Path(url.path), + sourceTree: .group, + sourceRoot: rootPath, + override: false, + validatePresence: true + ) + newFile?.fileEncoding = 4 // UTF-8 + newFile?.name = url.lastPathComponent - if let file = newFile, let buildPhase = myTarget.buildPhases.first(where: { $0.buildPhase == .sources }) { - _ = try buildPhase.add(file: file) + if let file = newFile, let buildPhase = myTarget.buildPhases.first(where: { $0.buildPhase == .sources }) { + _ = try buildPhase.add(file: file) + } } - } - func save() throws { - try xcodeproj.write(path: xcodeprojPath) + func save() throws { + try xcodeproj.write(path: xcodeprojPath) + } } -} +#endif diff --git a/Sources/ExFigCLI/Pipeline/SharedDownloadQueue.swift b/Sources/ExFigCLI/Pipeline/SharedDownloadQueue.swift index 75d63861..e873d8dd 100644 --- a/Sources/ExFigCLI/Pipeline/SharedDownloadQueue.swift +++ b/Sources/ExFigCLI/Pipeline/SharedDownloadQueue.swift @@ -1,7 +1,7 @@ import ExFigCore import Foundation import Logging -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/ExFigCLI/Subcommands/Export/PluginColorsExport.swift b/Sources/ExFigCLI/Subcommands/Export/PluginColorsExport.swift index 6fa3e0cc..dfaeb3b0 100644 --- a/Sources/ExFigCLI/Subcommands/Export/PluginColorsExport.swift +++ b/Sources/ExFigCLI/Subcommands/Export/PluginColorsExport.swift @@ -55,26 +55,28 @@ extension ExFigCommand.ExportColors { try await syncCodeSyntaxIfNeeded(entries: entries, client: client, ui: ui) // Post-export: update Xcode project (only if not in Swift Package) - if ios.xcassetsInSwiftPackage != true { - do { - let xcodeProject = try XcodeProjectWriter( - xcodeProjPath: ios.xcodeprojPath, - target: ios.target - ) - // Add Swift file references for each entry - for entry in entries { - if let url = entry.colorSwiftURL { - try xcodeProject.addFileReferenceToXcodeProj(url) - } - if let url = entry.swiftuiColorSwiftURL { - try xcodeProject.addFileReferenceToXcodeProj(url) + #if canImport(XcodeProj) + if ios.xcassetsInSwiftPackage != true { + do { + let xcodeProject = try XcodeProjectWriter( + xcodeProjPath: ios.xcodeprojPath, + target: ios.target + ) + // Add Swift file references for each entry + for entry in entries { + if let url = entry.colorSwiftURL { + try xcodeProject.addFileReferenceToXcodeProj(url) + } + if let url = entry.swiftuiColorSwiftURL { + try xcodeProject.addFileReferenceToXcodeProj(url) + } } + try xcodeProject.save() + } catch { + ui.warning(.xcodeProjectUpdateFailed(detail: error.localizedDescription)) } - try xcodeProject.save() - } catch { - ui.warning(.xcodeProjectUpdateFailed(detail: error.localizedDescription)) } - } + #endif // Check for updates (only in standalone mode) if !batchMode { diff --git a/Sources/ExFigCLI/Subcommands/Export/PluginIconsExport.swift b/Sources/ExFigCLI/Subcommands/Export/PluginIconsExport.swift index f7a18659..153b69b8 100644 --- a/Sources/ExFigCLI/Subcommands/Export/PluginIconsExport.swift +++ b/Sources/ExFigCLI/Subcommands/Export/PluginIconsExport.swift @@ -64,25 +64,27 @@ extension ExFigCommand.ExportIcons { } // Post-export: update Xcode project (only if not in Swift Package) - if ios.xcassetsInSwiftPackage != true { - do { - let xcodeProject = try XcodeProjectWriter( - xcodeProjPath: ios.xcodeprojPath, - target: ios.target - ) - for entry in entries { - if let url = entry.imageSwiftURL { - try xcodeProject.addFileReferenceToXcodeProj(url) - } - if let url = entry.swiftUIImageSwiftURL { - try xcodeProject.addFileReferenceToXcodeProj(url) + #if canImport(XcodeProj) + if ios.xcassetsInSwiftPackage != true { + do { + let xcodeProject = try XcodeProjectWriter( + xcodeProjPath: ios.xcodeprojPath, + target: ios.target + ) + for entry in entries { + if let url = entry.imageSwiftURL { + try xcodeProject.addFileReferenceToXcodeProj(url) + } + if let url = entry.swiftUIImageSwiftURL { + try xcodeProject.addFileReferenceToXcodeProj(url) + } } + try xcodeProject.save() + } catch { + ui.warning(.xcodeProjectUpdateFailed(detail: error.localizedDescription)) } - try xcodeProject.save() - } catch { - ui.warning(.xcodeProjectUpdateFailed(detail: error.localizedDescription)) } - } + #endif // Check for updates (only in standalone mode) if !batchMode { diff --git a/Sources/ExFigCLI/Subcommands/Export/PluginImagesExport.swift b/Sources/ExFigCLI/Subcommands/Export/PluginImagesExport.swift index 12d36f8c..91fb562f 100644 --- a/Sources/ExFigCLI/Subcommands/Export/PluginImagesExport.swift +++ b/Sources/ExFigCLI/Subcommands/Export/PluginImagesExport.swift @@ -63,25 +63,27 @@ extension ExFigCommand.ExportImages { } // Post-export: update Xcode project (only if not in Swift Package) - if ios.xcassetsInSwiftPackage != true { - do { - let xcodeProject = try XcodeProjectWriter( - xcodeProjPath: ios.xcodeprojPath, - target: ios.target - ) - for entry in entries { - if let url = entry.imageSwiftURL { - try xcodeProject.addFileReferenceToXcodeProj(url) - } - if let url = entry.swiftUIImageSwiftURL { - try xcodeProject.addFileReferenceToXcodeProj(url) + #if canImport(XcodeProj) + if ios.xcassetsInSwiftPackage != true { + do { + let xcodeProject = try XcodeProjectWriter( + xcodeProjPath: ios.xcodeprojPath, + target: ios.target + ) + for entry in entries { + if let url = entry.imageSwiftURL { + try xcodeProject.addFileReferenceToXcodeProj(url) + } + if let url = entry.swiftUIImageSwiftURL { + try xcodeProject.addFileReferenceToXcodeProj(url) + } } + try xcodeProject.save() + } catch { + ui.warning(.xcodeProjectUpdateFailed(detail: error.localizedDescription)) } - try xcodeProject.save() - } catch { - ui.warning(.xcodeProjectUpdateFailed(detail: error.localizedDescription)) } - } + #endif // Check for updates (only in standalone mode) if !batchMode { diff --git a/Sources/ExFigCLI/Subcommands/Export/PluginTypographyExport.swift b/Sources/ExFigCLI/Subcommands/Export/PluginTypographyExport.swift index 5ae8b4f4..9650f456 100644 --- a/Sources/ExFigCLI/Subcommands/Export/PluginTypographyExport.swift +++ b/Sources/ExFigCLI/Subcommands/Export/PluginTypographyExport.swift @@ -56,27 +56,29 @@ extension ExFigCommand.ExportTypography { ) // Post-export: update Xcode project (only if not in Swift Package) - if ios.xcassetsInSwiftPackage != true { - do { - let xcodeProject = try XcodeProjectWriter( - xcodeProjPath: ios.xcodeprojPath, - target: ios.target - ) - // Add Swift file references - if let url = pluginEntry.fontSwiftURL { - try xcodeProject.addFileReferenceToXcodeProj(url) + #if canImport(XcodeProj) + if ios.xcassetsInSwiftPackage != true { + do { + let xcodeProject = try XcodeProjectWriter( + xcodeProjPath: ios.xcodeprojPath, + target: ios.target + ) + // Add Swift file references + if let url = pluginEntry.fontSwiftURL { + try xcodeProject.addFileReferenceToXcodeProj(url) + } + if let url = pluginEntry.swiftUIFontSwiftURL { + try xcodeProject.addFileReferenceToXcodeProj(url) + } + if let url = pluginEntry.labelStyleSwiftURL { + try xcodeProject.addFileReferenceToXcodeProj(url) + } + try xcodeProject.save() + } catch { + input.ui.warning(.xcodeProjectUpdateFailed(detail: error.localizedDescription)) } - if let url = pluginEntry.swiftUIFontSwiftURL { - try xcodeProject.addFileReferenceToXcodeProj(url) - } - if let url = pluginEntry.labelStyleSwiftURL { - try xcodeProject.addFileReferenceToXcodeProj(url) - } - try xcodeProject.save() - } catch { - input.ui.warning(.xcodeProjectUpdateFailed(detail: error.localizedDescription)) } - } + #endif // Check for updates (only in standalone mode) if !batchMode { diff --git a/Sources/ExFigCLI/Subcommands/Export/iOSColorsExport.swift b/Sources/ExFigCLI/Subcommands/Export/iOSColorsExport.swift index d30459d2..06b4afb4 100644 --- a/Sources/ExFigCLI/Subcommands/Export/iOSColorsExport.swift +++ b/Sources/ExFigCLI/Subcommands/Export/iOSColorsExport.swift @@ -52,19 +52,21 @@ extension ExFigCommand.ExportColors { return } - do { - let xcodeProject = try XcodeProjectWriter( - xcodeProjPath: ios.xcodeprojPath, - target: ios.target - ) - try files.forEach { file in - if file.destination.file.pathExtension == "swift" { - try xcodeProject.addFileReferenceToXcodeProj(file.destination.url) + #if canImport(XcodeProj) + do { + let xcodeProject = try XcodeProjectWriter( + xcodeProjPath: ios.xcodeprojPath, + target: ios.target + ) + try files.forEach { file in + if file.destination.file.pathExtension == "swift" { + try xcodeProject.addFileReferenceToXcodeProj(file.destination.url) + } } + try xcodeProject.save() + } catch { + ui.warning(.xcodeProjectUpdateFailed(detail: error.localizedDescription)) } - try xcodeProject.save() - } catch { - ui.warning(.xcodeProjectUpdateFailed(detail: error.localizedDescription)) - } + #endif } } diff --git a/Sources/FigmaAPI/Client.swift b/Sources/FigmaAPI/Client.swift index d80d36d9..bc5df9d7 100644 --- a/Sources/FigmaAPI/Client.swift +++ b/Sources/FigmaAPI/Client.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/Endpoint/BaseEndpoint.swift b/Sources/FigmaAPI/Endpoint/BaseEndpoint.swift index 9405bec6..5f1c9eee 100644 --- a/Sources/FigmaAPI/Endpoint/BaseEndpoint.swift +++ b/Sources/FigmaAPI/Endpoint/BaseEndpoint.swift @@ -1,6 +1,6 @@ import ExFigCore import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/Endpoint/ComponentsEndpoint.swift b/Sources/FigmaAPI/Endpoint/ComponentsEndpoint.swift index 8cd95d9b..b7f9ce1e 100644 --- a/Sources/FigmaAPI/Endpoint/ComponentsEndpoint.swift +++ b/Sources/FigmaAPI/Endpoint/ComponentsEndpoint.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/Endpoint/Endpoint.swift b/Sources/FigmaAPI/Endpoint/Endpoint.swift index 36345116..d712cc05 100644 --- a/Sources/FigmaAPI/Endpoint/Endpoint.swift +++ b/Sources/FigmaAPI/Endpoint/Endpoint.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/Endpoint/FileMetadataEndpoint.swift b/Sources/FigmaAPI/Endpoint/FileMetadataEndpoint.swift index 11b31af7..dc813e7c 100644 --- a/Sources/FigmaAPI/Endpoint/FileMetadataEndpoint.swift +++ b/Sources/FigmaAPI/Endpoint/FileMetadataEndpoint.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/Endpoint/ImageEndpoint.swift b/Sources/FigmaAPI/Endpoint/ImageEndpoint.swift index 38726b0e..7eff782b 100644 --- a/Sources/FigmaAPI/Endpoint/ImageEndpoint.swift +++ b/Sources/FigmaAPI/Endpoint/ImageEndpoint.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/Endpoint/LatestReleaseEndpoint.swift b/Sources/FigmaAPI/Endpoint/LatestReleaseEndpoint.swift index 04b32312..9983be92 100644 --- a/Sources/FigmaAPI/Endpoint/LatestReleaseEndpoint.swift +++ b/Sources/FigmaAPI/Endpoint/LatestReleaseEndpoint.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/Endpoint/NodesEndpoint.swift b/Sources/FigmaAPI/Endpoint/NodesEndpoint.swift index 2eebcf02..a04f0ed4 100644 --- a/Sources/FigmaAPI/Endpoint/NodesEndpoint.swift +++ b/Sources/FigmaAPI/Endpoint/NodesEndpoint.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/Endpoint/StylesEndpoint.swift b/Sources/FigmaAPI/Endpoint/StylesEndpoint.swift index e2189986..032e892b 100644 --- a/Sources/FigmaAPI/Endpoint/StylesEndpoint.swift +++ b/Sources/FigmaAPI/Endpoint/StylesEndpoint.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/Endpoint/UpdateVariablesEndpoint.swift b/Sources/FigmaAPI/Endpoint/UpdateVariablesEndpoint.swift index 381b4ecd..6a26426a 100644 --- a/Sources/FigmaAPI/Endpoint/UpdateVariablesEndpoint.swift +++ b/Sources/FigmaAPI/Endpoint/UpdateVariablesEndpoint.swift @@ -1,6 +1,6 @@ import ExFigCore import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/Endpoint/VariablesEndpoint.swift b/Sources/FigmaAPI/Endpoint/VariablesEndpoint.swift index c14961e9..a602526e 100644 --- a/Sources/FigmaAPI/Endpoint/VariablesEndpoint.swift +++ b/Sources/FigmaAPI/Endpoint/VariablesEndpoint.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/FigmaAPIError.swift b/Sources/FigmaAPI/FigmaAPIError.swift index dac5d498..9a952b6c 100644 --- a/Sources/FigmaAPI/FigmaAPIError.swift +++ b/Sources/FigmaAPI/FigmaAPIError.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/FigmaClient.swift b/Sources/FigmaAPI/FigmaClient.swift index 78ea64ca..142d01b9 100644 --- a/Sources/FigmaAPI/FigmaClient.swift +++ b/Sources/FigmaAPI/FigmaClient.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/GitHubClient.swift b/Sources/FigmaAPI/GitHubClient.swift index 79f7f334..efa66165 100644 --- a/Sources/FigmaAPI/GitHubClient.swift +++ b/Sources/FigmaAPI/GitHubClient.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/RateLimitedClient.swift b/Sources/FigmaAPI/RateLimitedClient.swift index b91ffccb..6d2d4518 100644 --- a/Sources/FigmaAPI/RateLimitedClient.swift +++ b/Sources/FigmaAPI/RateLimitedClient.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/FigmaAPI/RetryPolicy.swift b/Sources/FigmaAPI/RetryPolicy.swift index 555be120..dd38729d 100644 --- a/Sources/FigmaAPI/RetryPolicy.swift +++ b/Sources/FigmaAPI/RetryPolicy.swift @@ -1,5 +1,5 @@ import Foundation -#if os(Linux) +#if canImport(FoundationNetworking) import FoundationNetworking #endif diff --git a/Sources/SVGKit/SVGParser.swift b/Sources/SVGKit/SVGParser.swift index 1e1d1731..8d49ceca 100644 --- a/Sources/SVGKit/SVGParser.swift +++ b/Sources/SVGKit/SVGParser.swift @@ -2,7 +2,7 @@ import Foundation import Logging import Resvg -#if os(Linux) +#if canImport(FoundationXML) import FoundationXML #endif From 9a2f3507a0d4d82a624f43d7b45867c4c7e23468 Mon Sep 17 00:00:00 2001 From: alexey1312 Date: Tue, 24 Feb 2026 09:48:45 +0500 Subject: [PATCH 2/2] chore: archive add-windows-support openspec change Co-Authored-By: Claude Opus 4.6 --- .../2026-02-24-add-windows-support}/design.md | 0 .../2026-02-24-add-windows-support}/proposal.md | 0 .../specs/platform-support/spec.md | 0 .../2026-02-24-add-windows-support}/tasks.md | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename openspec/changes/{add-windows-support => archive/2026-02-24-add-windows-support}/design.md (100%) rename openspec/changes/{add-windows-support => archive/2026-02-24-add-windows-support}/proposal.md (100%) rename openspec/changes/{add-windows-support => archive/2026-02-24-add-windows-support}/specs/platform-support/spec.md (100%) rename openspec/changes/{add-windows-support => archive/2026-02-24-add-windows-support}/tasks.md (100%) diff --git a/openspec/changes/add-windows-support/design.md b/openspec/changes/archive/2026-02-24-add-windows-support/design.md similarity index 100% rename from openspec/changes/add-windows-support/design.md rename to openspec/changes/archive/2026-02-24-add-windows-support/design.md diff --git a/openspec/changes/add-windows-support/proposal.md b/openspec/changes/archive/2026-02-24-add-windows-support/proposal.md similarity index 100% rename from openspec/changes/add-windows-support/proposal.md rename to openspec/changes/archive/2026-02-24-add-windows-support/proposal.md diff --git a/openspec/changes/add-windows-support/specs/platform-support/spec.md b/openspec/changes/archive/2026-02-24-add-windows-support/specs/platform-support/spec.md similarity index 100% rename from openspec/changes/add-windows-support/specs/platform-support/spec.md rename to openspec/changes/archive/2026-02-24-add-windows-support/specs/platform-support/spec.md diff --git a/openspec/changes/add-windows-support/tasks.md b/openspec/changes/archive/2026-02-24-add-windows-support/tasks.md similarity index 100% rename from openspec/changes/add-windows-support/tasks.md rename to openspec/changes/archive/2026-02-24-add-windows-support/tasks.md