SPB Git

spb/zyquo-cloud

Public MIT

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

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 (Jul 30, 2026) parent 41b9e85 Browse files

8 changed files +277 −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 down to 3 models; Perplexity has no `/models` (built-in catalog: sonar, sonar-pro, sonar-reasoning-pro, 16 down to 3 models; Perplexity has no `/models` (built-in catalog: sonar, sonar-pro, sonar-reasoning-pro,
17 sonar-deep-research). Items unverifiable from docs are flagged inline and queued for Phase 7. 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 21+- [x] `Package.swift` — executable targets `ZyquoCloud` + `zyquo-verify`, test target, macOS 13+, swift-markdown dep
22−- [ ] Minimal `@main` SwiftUI app that builds and launches 22+- [x] Minimal `@main` SwiftUI app that builds and launches (activation policy handled)
23−- [ ] `Makefile`: `make dev` (build + assemble `Zyquo Cloud.app` + ad-hoc sign), Info.plist per spec 23+- [x] `Makefile`: `make dev` (release build + assemble `Zyquo Cloud.app` + ad-hoc sign), `make release`
24−- [ ] Phase checkpoint: `swift build` clean, app launches from Finder/`open` 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 ## Phase 2 — Architecture (pending) 40 ## Phase 2 — Architecture (pending)
26 ## Phase 3 — SecureKeyStore (pending) 41 ## Phase 3 — SecureKeyStore (pending)
27 ## Phase 4 — Design System (pending) 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
Menu

Theme