SPB Git

spb/zyquo-cloud Public MIT

Native macOS AI chat client for 12 cloud providers — your keys, every cloud model, one beautiful chat.

Swift 97.4% Shell 1.7% Makefile 1%

phase1: SPM package, minimal SwiftUI app, Makefile + bundle assembly (make dev verified)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
simon-pierre boucher committed 11 days ago (Jul 30, 2026) parent 41b9e85

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