phase1: SPM package, minimal SwiftUI app, Makefile + bundle assembly (make dev verified)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Showing 8 changed files with +277 and −5
added
Makefile
+81 −0
@@ -0,0 +1,81 @@ | ||
| 1 | +# | |
| 2 | +# Makefile | |
| 3 | +# Zyquo Cloud | |
| 4 | +# | |
| 5 | +# Author: Simon-Pierre Boucher | |
| 6 | +# Mail: contact@spboucher.ai | |
| 7 | +# | |
| 8 | +# make / make dev → release build, assemble dist/Zyquo Cloud.app, ad-hoc sign (fast local iteration) | |
| 9 | +# make release → universal binary, Developer ID sign, notarize, staple (Phase 8) | |
| 10 | +# make icon → regenerate AppIcon.icns from assets/icon/zyquo-cloud.svg (Phase 5) | |
| 11 | +# make test → swift test | |
| 12 | +# make clean → remove build products | |
| 13 | +# | |
| 14 | + | |
| 15 | +APP_NAME := Zyquo Cloud | |
| 16 | +EXEC_NAME := ZyquoCloud | |
| 17 | +BUNDLE_ID := com.zyquo.cloud | |
| 18 | +VERSION := 1.0.0 | |
| 19 | +BUILD_NUM := 1 | |
| 20 | +MIN_MACOS := 13.0 | |
| 21 | +DIST := dist | |
| 22 | +APP_DIR := $(DIST)/$(APP_NAME).app | |
| 23 | +IDENTITY := Developer ID Application: Simon-Pierre Boucher (3YM54G49SN) | |
| 24 | +NOTARY_PROFILE:= MacLustr-Notarize | |
| 25 | +ENTITLEMENTS := Resources/ZyquoCloud.entitlements | |
| 26 | + | |
| 27 | +.PHONY: dev build bundle release universal icon test clean run verify | |
| 28 | + | |
| 29 | +dev: build bundle | |
| 30 | + @echo "=== Ad-hoc signing (dev) ===" | |
| 31 | + codesign --force --deep --sign - "$(APP_DIR)" | |
| 32 | + codesign --verify --deep --strict "$(APP_DIR)" | |
| 33 | + @echo "Packaged: $(APP_DIR) — launch with: open \"$(APP_DIR)\"" | |
| 34 | + | |
| 35 | +build: | |
| 36 | + swift build -c release | |
| 37 | + | |
| 38 | +test: | |
| 39 | + swift test | |
| 40 | + | |
| 41 | +run: dev | |
| 42 | + open "$(APP_DIR)" | |
| 43 | + | |
| 44 | +bundle: | |
| 45 | + @echo "=== Assembling $(APP_DIR) ===" | |
| 46 | + rm -rf "$(APP_DIR)" | |
| 47 | + mkdir -p "$(APP_DIR)/Contents/MacOS" "$(APP_DIR)/Contents/Resources" | |
| 48 | + cp .build/release/$(EXEC_NAME) "$(APP_DIR)/Contents/MacOS/$(EXEC_NAME)" | |
| 49 | + @for b in .build/release/*.bundle; do [ -e "$$b" ] && cp -R "$$b" "$(APP_DIR)/Contents/Resources/" || true; done | |
| 50 | + @if [ -f Resources/AppIcon.icns ]; then cp Resources/AppIcon.icns "$(APP_DIR)/Contents/Resources/AppIcon.icns"; fi | |
| 51 | + scripts/write-info-plist.sh "$(APP_DIR)" "$(APP_NAME)" "$(EXEC_NAME)" "$(BUNDLE_ID)" "$(VERSION)" "$(BUILD_NUM)" "$(MIN_MACOS)" | |
| 52 | + | |
| 53 | +universal: | |
| 54 | + @echo "=== Building universal binary (arm64 + x86_64) ===" | |
| 55 | + swift build -c release --arch arm64 | |
| 56 | + swift build -c release --arch x86_64 | |
| 57 | + mkdir -p .build/universal | |
| 58 | + lipo -create \ | |
| 59 | + .build/arm64-apple-macosx/release/$(EXEC_NAME) \ | |
| 60 | + .build/x86_64-apple-macosx/release/$(EXEC_NAME) \ | |
| 61 | + -output .build/universal/$(EXEC_NAME) | |
| 62 | + | |
| 63 | +release: universal | |
| 64 | + @echo "=== Assembling $(APP_DIR) (universal) ===" | |
| 65 | + rm -rf "$(APP_DIR)" | |
| 66 | + mkdir -p "$(APP_DIR)/Contents/MacOS" "$(APP_DIR)/Contents/Resources" | |
| 67 | + cp .build/universal/$(EXEC_NAME) "$(APP_DIR)/Contents/MacOS/$(EXEC_NAME)" | |
| 68 | + @for b in .build/arm64-apple-macosx/release/*.bundle; do [ -e "$$b" ] && cp -R "$$b" "$(APP_DIR)/Contents/Resources/" || true; done | |
| 69 | + @if [ -f Resources/AppIcon.icns ]; then cp Resources/AppIcon.icns "$(APP_DIR)/Contents/Resources/AppIcon.icns"; fi | |
| 70 | + scripts/write-info-plist.sh "$(APP_DIR)" "$(APP_NAME)" "$(EXEC_NAME)" "$(BUNDLE_ID)" "$(VERSION)" "$(BUILD_NUM)" "$(MIN_MACOS)" | |
| 71 | + scripts/notarize.sh "$(APP_DIR)" "$(IDENTITY)" "$(NOTARY_PROFILE)" "$(ENTITLEMENTS)" | |
| 72 | + | |
| 73 | +icon: | |
| 74 | + scripts/generate-icon.sh | |
| 75 | + | |
| 76 | +verify: | |
| 77 | + swift build -c release | |
| 78 | + .build/release/zyquo-verify | |
| 79 | + | |
| 80 | +clean: | |
| 81 | + rm -rf .build $(DIST) | |
added
Package.resolved
+23 −0
@@ -0,0 +1,23 @@ | ||
| 1 | +{ | |
| 2 | + "pins" : [ | |
| 3 | + { | |
| 4 | + "identity" : "swift-cmark", | |
| 5 | + "kind" : "remoteSourceControl", | |
| 6 | + "location" : "https://github.com/swiftlang/swift-cmark.git", | |
| 7 | + "state" : { | |
| 8 | + "revision" : "924936d0427cb25a61169739a7660230bffa6ea6", | |
| 9 | + "version" : "0.8.0" | |
| 10 | + } | |
| 11 | + }, | |
| 12 | + { | |
| 13 | + "identity" : "swift-markdown", | |
| 14 | + "kind" : "remoteSourceControl", | |
| 15 | + "location" : "https://github.com/swiftlang/swift-markdown.git", | |
| 16 | + "state" : { | |
| 17 | + "revision" : "3c6f9523da3a1ec2fd829673e472d95b8097a3b8", | |
| 18 | + "version" : "0.8.0" | |
| 19 | + } | |
| 20 | + } | |
| 21 | + ], | |
| 22 | + "version" : 2 | |
| 23 | +} | |
added
Package.swift
+39 −0
@@ -0,0 +1,39 @@ | ||
| 1 | +// swift-tools-version:5.9 | |
| 2 | +// | |
| 3 | +// Package.swift | |
| 4 | +// Zyquo Cloud | |
| 5 | +// | |
| 6 | +// Author: Simon-Pierre Boucher | |
| 7 | +// Mail: contact@spboucher.ai | |
| 8 | +// | |
| 9 | + | |
| 10 | +import PackageDescription | |
| 11 | + | |
| 12 | +let package = Package( | |
| 13 | + name: "ZyquoCloud", | |
| 14 | + platforms: [ | |
| 15 | + .macOS(.v13) | |
| 16 | + ], | |
| 17 | + dependencies: [ | |
| 18 | + .package(url: "https://github.com/swiftlang/swift-markdown.git", from: "0.5.0") | |
| 19 | + ], | |
| 20 | + targets: [ | |
| 21 | + .executableTarget( | |
| 22 | + name: "ZyquoCloud", | |
| 23 | + dependencies: [ | |
| 24 | + .product(name: "Markdown", package: "swift-markdown") | |
| 25 | + ], | |
| 26 | + path: "Sources/ZyquoCloud" | |
| 27 | + ), | |
| 28 | + .executableTarget( | |
| 29 | + name: "zyquo-verify", | |
| 30 | + dependencies: [], | |
| 31 | + path: "Sources/ZyquoVerify" | |
| 32 | + ), | |
| 33 | + .testTarget( | |
| 34 | + name: "ZyquoCloudTests", | |
| 35 | + dependencies: ["ZyquoCloud"], | |
| 36 | + path: "Tests/ZyquoCloudTests" | |
| 37 | + ), | |
| 38 | + ] | |
| 39 | +) | |
added
Sources/ZyquoCloud/App/ZyquoCloudApp.swift
+31 −0
@@ -0,0 +1,31 @@ | ||
| 1 | +// | |
| 2 | +// ZyquoCloudApp.swift | |
| 3 | +// Zyquo Cloud | |
| 4 | +// | |
| 5 | +// Author: Simon-Pierre Boucher | |
| 6 | +// Mail: contact@spboucher.ai | |
| 7 | +// | |
| 8 | + | |
| 9 | +import SwiftUI | |
| 10 | + | |
| 11 | +@main | |
| 12 | +struct ZyquoCloudApp: App { | |
| 13 | + @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate | |
| 14 | + | |
| 15 | + var body: some Scene { | |
| 16 | + WindowGroup("Zyquo Cloud") { | |
| 17 | + Text("Zyquo Cloud") | |
| 18 | + .frame(minWidth: 980, minHeight: 640) | |
| 19 | + } | |
| 20 | + .defaultSize(width: 1240, height: 800) | |
| 21 | + } | |
| 22 | +} | |
| 23 | + | |
| 24 | +final class AppDelegate: NSObject, NSApplicationDelegate { | |
| 25 | + func applicationDidFinishLaunching(_ notification: Notification) { | |
| 26 | + // Ensure the app fronts correctly when launched outside Finder | |
| 27 | + // (e.g. `swift run` or `open` from a terminal during development). | |
| 28 | + NSApplication.shared.setActivationPolicy(.regular) | |
| 29 | + NSApplication.shared.activate(ignoringOtherApps: true) | |
| 30 | + } | |
| 31 | +} | |
added
Sources/ZyquoVerify/main.swift
+11 −0
@@ -0,0 +1,11 @@ | ||
| 1 | +// | |
| 2 | +// main.swift | |
| 3 | +// Zyquo Cloud | |
| 4 | +// | |
| 5 | +// Author: Simon-Pierre Boucher | |
| 6 | +// Mail: contact@spboucher.ai | |
| 7 | +// | |
| 8 | +// zyquo-verify — Phase 7 API verification harness (implemented in Phase 7). | |
| 9 | +// | |
| 10 | + | |
| 11 | +print("zyquo-verify: harness not yet implemented (Phase 7)") | |
added
Tests/ZyquoCloudTests/ZyquoCloudTests.swift
+15 −0
@@ -0,0 +1,15 @@ | ||
| 1 | +// | |
| 2 | +// ZyquoCloudTests.swift | |
| 3 | +// Zyquo Cloud | |
| 4 | +// | |
| 5 | +// Author: Simon-Pierre Boucher | |
| 6 | +// Mail: contact@spboucher.ai | |
| 7 | +// | |
| 8 | + | |
| 9 | +import XCTest | |
| 10 | + | |
| 11 | +final class ZyquoCloudTests: XCTestCase { | |
| 12 | + func testPlaceholder() { | |
| 13 | + XCTAssertTrue(true) | |
| 14 | + } | |
| 15 | +} | |
modified
docs/PLAN.md
+20 −5
@@ -16,12 +16,27 @@ Nemo 2026-07-31 (reasoning via `reasoning_effort`); xAI catalog now grok-4.5/4.3 | ||
| 16 | 16 | down to 3 models; Perplexity has no `/models` (built-in catalog: sonar, sonar-pro, sonar-reasoning-pro, |
| 17 | 17 | sonar-deep-research). Items unverifiable from docs are flagged inline and queued for Phase 7. |
| 18 | 18 | |
| 19 | −## Phase 1 — Project Setup (in progress) | |
| 19 | +## Phase 1 — Project Setup ✅ (completed 2026-07-30) | |
| 20 | 20 | |
| 21 | −- [ ] `Package.swift` — executable target `ZyquoCloud`, macOS 13+, swift-markdown dep | |
| 22 | −- [ ] Minimal `@main` SwiftUI app that builds and launches | |
| 23 | −- [ ] `Makefile`: `make dev` (build + assemble `Zyquo Cloud.app` + ad-hoc sign), Info.plist per spec | |
| 24 | −- [ ] Phase checkpoint: `swift build` clean, app launches from Finder/`open` | |
| 21 | +- [x] `Package.swift` — executable targets `ZyquoCloud` + `zyquo-verify`, test target, macOS 13+, swift-markdown dep | |
| 22 | +- [x] Minimal `@main` SwiftUI app that builds and launches (activation policy handled) | |
| 23 | +- [x] `Makefile`: `make dev` (release build + assemble `Zyquo Cloud.app` + ad-hoc sign), `make release` | |
| 24 | + (universal + Developer ID + notarize skeleton, completed in Phase 8), `scripts/write-info-plist.sh` | |
| 25 | +- [x] Phase checkpoint: `swift build` clean (0 warnings), `make dev` assembles bundle, app launched via | |
| 26 | + `open`, process verified, quit cleanly | |
| 27 | + | |
| 28 | +**Phase 1 checkpoint summary:** SPM-only toolchain working on Swift 6.4. `make dev` produces an | |
| 29 | +ad-hoc-signed `dist/Zyquo Cloud.app` with correct Info.plist (`com.zyquo.cloud`, macOS 13+, | |
| 30 | +productivity category). Release/notarization targets stubbed to `scripts/notarize.sh` using the | |
| 31 | +verified zyquo-term identity + `MacLustr-Notarize` profile (implemented fully in Phase 8). | |
| 32 | + | |
| 33 | +## Phase 2 — Architecture (in progress) | |
| 34 | + | |
| 35 | +- [ ] Models: `ProviderID`, `AIModel`, `ModelCapabilities`, `ModelPricing`, `ParameterSupport`, `Message`, `Conversation`, `Persona`, `ChatParameters`, `TokenUsage` | |
| 36 | +- [ ] Providers: `ProviderProtocol` (`ChatRequest` → `AsyncThrowingStream<ChatEvent>`), `OpenAICompatibleClient` (10+ providers incl. Gemini-compat), `AnthropicClient` | |
| 37 | +- [ ] Services: `StreamingService` (SSE parser), `ModelCatalog` (from PROVIDERS.md), `PersistenceService` (JSON in Application Support) | |
| 38 | +- [ ] ViewModels/Views skeleton folders | |
| 39 | +- [ ] Phase checkpoint: builds clean, unit tests for SSE parser + models | |
| 25 | 40 | ## Phase 2 — Architecture (pending) |
| 26 | 41 | ## Phase 3 — SecureKeyStore (pending) |
| 27 | 42 | ## Phase 4 — Design System (pending) |
added
scripts/write-info-plist.sh
+57 −0
@@ -0,0 +1,57 @@ | ||
| 1 | +#!/bin/bash | |
| 2 | +# | |
| 3 | +# write-info-plist.sh | |
| 4 | +# Zyquo Cloud | |
| 5 | +# | |
| 6 | +# Author: Simon-Pierre Boucher | |
| 7 | +# Mail: contact@spboucher.ai | |
| 8 | +# | |
| 9 | +# Writes Contents/Info.plist into an assembled app bundle. | |
| 10 | +# Usage: write-info-plist.sh <app-dir> <app-name> <exec-name> <bundle-id> <version> <build> <min-macos> | |
| 11 | +# | |
| 12 | +set -euo pipefail | |
| 13 | + | |
| 14 | +APP_DIR="$1"; APP_NAME="$2"; EXEC_NAME="$3"; BUNDLE_ID="$4"; VERSION="$5"; BUILD_NUM="$6"; MIN_MACOS="$7" | |
| 15 | + | |
| 16 | +cat > "$APP_DIR/Contents/Info.plist" << PLIST | |
| 17 | +<?xml version="1.0" encoding="UTF-8"?> | |
| 18 | +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | |
| 19 | +<plist version="1.0"> | |
| 20 | +<dict> | |
| 21 | + <key>CFBundleName</key> | |
| 22 | + <string>${APP_NAME}</string> | |
| 23 | + <key>CFBundleDisplayName</key> | |
| 24 | + <string>${APP_NAME}</string> | |
| 25 | + <key>CFBundleIdentifier</key> | |
| 26 | + <string>${BUNDLE_ID}</string> | |
| 27 | + <key>CFBundleShortVersionString</key> | |
| 28 | + <string>${VERSION}</string> | |
| 29 | + <key>CFBundleVersion</key> | |
| 30 | + <string>${BUILD_NUM}</string> | |
| 31 | + <key>CFBundleExecutable</key> | |
| 32 | + <string>${EXEC_NAME}</string> | |
| 33 | + <key>CFBundlePackageType</key> | |
| 34 | + <string>APPL</string> | |
| 35 | + <key>CFBundleIconFile</key> | |
| 36 | + <string>AppIcon</string> | |
| 37 | + <key>CFBundleInfoDictionaryVersion</key> | |
| 38 | + <string>6.0</string> | |
| 39 | + <key>LSMinimumSystemVersion</key> | |
| 40 | + <string>${MIN_MACOS}</string> | |
| 41 | + <key>NSHighResolutionCapable</key> | |
| 42 | + <true/> | |
| 43 | + <key>NSPrincipalClass</key> | |
| 44 | + <string>NSApplication</string> | |
| 45 | + <key>LSApplicationCategoryType</key> | |
| 46 | + <string>public.app-category.productivity</string> | |
| 47 | + <key>NSHumanReadableCopyright</key> | |
| 48 | + <string>© 2026 Simon-Pierre Boucher. All rights reserved.</string> | |
| 49 | + <key>NSSupportsAutomaticTermination</key> | |
| 50 | + <false/> | |
| 51 | + <key>NSSupportsSuddenTermination</key> | |
| 52 | + <false/> | |
| 53 | +</dict> | |
| 54 | +</plist> | |
| 55 | +PLIST | |
| 56 | + | |
| 57 | +echo "Info.plist written to $APP_DIR/Contents/Info.plist" | |
| 58 | ||