commit 30290801fe111614657d23e7b72642a1c7dcf10b Author: TZGyn Date: Mon Sep 28 12:23:22 2026 +0800 Add the TZGyn dev skill diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..6880f78 --- /dev/null +++ b/README.md @@ -0,0 +1,26 @@ +# TZGyn-skill + +My personal Crush skill: conventions for starting a project on Gitea, publishing +to the TZGyn Homebrew tap (casks for GUI apps, formulae for CLIs), and signing / +notarizing / releasing macOS apps. + +See [SKILL.md](./SKILL.md). + +## Install (global Crush skill) + +```bash +git clone https://gitea.tzgyn.dev/TZGyn/TZGyn-skill.git ~/Git/TZGyn-skill +mkdir -p ~/.agents/skills +ln -s ~/Git/TZGyn-skill ~/.agents/skills/TZGyn-skill +``` + +The symlink means edits to this repo are the live skill. Start a new Crush +session (or run `crush_info`) to see it loaded. + +## Updating + +```bash +cd ~/Git/TZGyn-skill +# edit SKILL.md +git commit -am "" && git push +``` diff --git a/SKILL.md b/SKILL.md new file mode 100644 index 0000000..e4ec838 --- /dev/null +++ b/SKILL.md @@ -0,0 +1,199 @@ +--- +name: TZGyn-skill +description: Use for any TZGyn development workflow — starting a new project on Gitea, publishing to the TZGyn Homebrew tap (casks for GUI apps, formulae for CLIs), signing and notarizing a desktop/mobile build, cutting a release, or extending/updating this skill itself. +--- + +# TZGyn dev skill + +Personal conventions for TZGyn projects. Source of truth is the Git repo +`https://gitea.tzgyn.dev/TZGyn/TZGyn-skill`; it is installed as a global Crush +skill at `~/.agents/skills/TZGyn-skill` (a symlink to the checkout, so editing +the repo updates the live skill). + +The reference implementations to copy from are `~/Git/kapture` and +`~/Git/konference` (macOS menu-bar apps). + +## Ground rules + +- **Host**: Gitea at `https://gitea.tzgyn.dev`, owner `TZGyn`, `tea` login name + `gitea.tzgyn.dev`. SSH: `git@gitea.tzgyn.dev:TZGyn/.git`. +- **Always print the upgrade script** (the `brew` commands below) in the final + answer after any release or version bump. The user wants it every time. +- Never commit build artifacts. `.gitignore` covers `.build/`, `*.app/`, + `dist/`, `.swiftpm/`, `xcuserdata/`, `DerivedData/`, `.DS_Store`. +- Never commit secrets (`.p8`, API keys) or push to a remote unless asked. +- Commit messages: concise, why-focused, first line < 72 chars; add a body only + when it helps a future reader. +- Prefer SwiftPM (Swift/SwiftUI) for macOS apps; no external dependencies. + +## 1. New project on Gitea + +```bash +# 1. Create the repo (as the authenticated user). +tea repos create --name --desc "" --login gitea.tzgyn.dev +# Do NOT pass --owner TZGyn: that hits /orgs/TZGyn/repos and 404s for a user. + +# 2. Initialise and push. +cd ~/Git/ +git init -b main +git add -A && git commit -m "" +git remote add origin https://gitea.tzgyn.dev/TZGyn/.git +git push -u origin main +``` + +Scaffold every repo with `README.md`, `AGENTS.md` (architecture + commands + +gotchas), and `.gitignore`. See `~/Git/simpletopbar` for the minimal shape: +`Package.swift`, `Sources//*.swift`, `build-app.sh`, `release.sh`, +`scripts/`. + +## 2. Homebrew tap + +- The tap clone lives at **`~/.cache/homebrew-tap`** + (origin `https://gitea.tzgyn.dev/TZGyn/homebrew-tap.git`). +- Layout: **GUI apps → `Casks/.rb`**; **CLI tools → `Formula/.rb`**. +- After pushing, sync Homebrew's own clone: + + ```bash + git -C ~/.cache/homebrew-tap add -A + git -C ~/.cache/homebrew-tap commit -m "Add cask" + git -C ~/.cache/homebrew-tap push + git -C /opt/homebrew/Library/Taps/tzgyn/homebrew-tap pull --ff-only + brew audit --cask tzgyn/tap/ # or: brew audit --formula tzgyn/tap/ + ``` + +- Users tap once (non-GitHub taps need the full URL): + + ```bash + brew tap TZGyn/tap https://gitea.tzgyn.dev/TZGyn/homebrew-tap.git + brew trust TZGyn/tap + ``` + +### Cask (GUI app) + +```ruby +cask "" do + version "X.Y.Z" + sha256 "" + + url "https://gitea.tzgyn.dev/TZGyn//releases/download/v#{version}/.zip" + name "" + desc "" + homepage "https://gitea.tzgyn.dev/TZGyn/" + + app ".app" +end +``` + +### Formula (CLI) + +```ruby +class < Formula + desc "" + homepage "https://gitea.tzgyn.dev/TZGyn/" + url "https://gitea.tzgyn.dev/TZGyn//releases/download/v0.1.0/-0.1.0-darwin-arm64.tar.gz" + sha256 "" + version "0.1.0" + + def install + bin.install "" + end +end +``` + +## 3. Releasing a macOS GUI app + +Two scripts per repo: `build-app.sh ` and `release.sh `. + +1. **Build + sign** (`build-app.sh`): `swift build -c release` → handwritten + `Info.plist` (NOT generated from `Package.swift`) → `make-icon.sh` → sign + with the Developer ID identity resolved from the login keychain + (`security find-identity -v -p codesigning | awk '/Developer ID Application:/{print $2;exit}'`), + falling back to ad-hoc (`-`). GUI apps set `LSUIElement = true` and + `CFBundleIdentifier = dev.tzgyn.`. +2. **Notarize + staple + zip** (`release.sh`): build → `ditto -c -k --keepParent + .app dist/.zip` → submit to Apple → staple → re-zip → print sha256. + See [Notarization](#4-notarization) for the credentials. +3. **Publish the release** and attach the zip: + + ```bash + tea releases create --tag v --title " " \ + --note "" --asset dist/.zip \ + --repo TZGyn/ --login gitea.tzgyn.dev + ``` + +4. **Bump the cask** (`version` + `sha256`), push the tap, pull Homebrew's + clone, `brew audit`. Verify by downloading the asset back and comparing the + sha256, and `spctl -a -vv .app` → `accepted` / + `source=Notarized Developer ID`. + +5. **Print the upgrade script**: + + ```bash + brew update + brew upgrade --cask + ``` + +## 4. Notarization + +Signing identity: `Developer ID Application: Teh Zi Gyn (RX6767S6BW)`. +App Store Connect API key (shared with Kapture): + +| Value | Where | +| --------- | ------------------------------------------------------------ | +| Key file | `~/.config/kapture/AuthKey_K5KXMMSB28.p8` | +| Key ID | `K5KXMMSB28` | +| Issuer ID | `3e25e5dc-8b75-453f-a71c-c7f3feaac628` (not secret) | +| Team ID | `RX6767S6BW` | + +Signing must use a hardened runtime and a secure timestamp: + +```bash +codesign --force --options runtime --timestamp --sign "$IDENT" ".app" +``` + +Submit and verify: + +```bash +xcrun notarytool submit dist/.zip \ + --key "$HOME/.config/kapture/AuthKey_K5KXMMSB28.p8" \ + --key-id K5KXMMSB28 --issuer 3e25e5dc-8b75-453f-a71c-c7f3feaac628 \ + --wait --output-format json +xcrun stapler staple .app && xcrun stapler validate .app +spctl -a -vv .app # want: accepted, source=Notarized Developer ID +``` + +Wire `release.sh` so it notarizes when `NOTARY_*` env vars are set and otherwise +skips gracefully (build + zip + sha only). An unnotarized app is still signed but +Gatekeeper warns on other machines; the user can bypass with +`xattr -dr com.apple.quarantine .app` or install with +`brew install --cask --no-quarantine `. + +## 5. Mobile + +For mobile (iOS/Android), notarization does not apply; the equivalent is store +signing (App Store Connect / Play Console) or EAS. Ask for the specific stack +before assuming; this skill currently only codifies the macOS path. + +## 6. Extending this skill (self-improvement) + +This skill is meant to grow. When the user asks for a new behaviour, preference, +or convention: + +1. Edit the relevant section of `SKILL.md` (add a new section if it is a new + topic). Keep it concrete: exact commands, paths, and templates. +2. Keep it truthful — only document what has actually worked; delete steps that + became stale (e.g. a changed path or flag). +3. Commit and push: + + ```bash + cd ~/Git/TZGyn-skill + git add -A && git commit -m "" + git push + ``` + +4. The installed copy is a symlink, so it updates immediately; a new Crush + session picks it up. Confirm with `crush_info` (the skill should appear under + `[skills]`). + +Prefer updating this skill over repeating ad-hoc instructions in a project: if a +convention applies to more than one repo, it belongs here.