This file is auto-loaded by Claude Code when working in this repository. Keep it concise and useful — it is not user-facing documentation.
Bokashi is a native macOS screenshot tool with annotation and privacy-masking features, distributed as OSS under MIT. The name comes from the Japanese word ぼかし (blur), reflecting the privacy-mask feature, which is the long-term differentiator against Shottr / CleanShot X / Xnapper / Flameshot.
The user-facing pitch is in README.md. The full roadmap is in
docs/ROADMAP.md.
- Language: Swift (latest stable). Use Swift Concurrency (
async/await) for capture and IO paths. - UI: SwiftUI primarily; drop to AppKit only when SwiftUI cannot express the needed behavior (transparent overlay windows for region selection, global hotkeys, menubar status item, etc.).
- Capture API:
ScreenCaptureKit. Do not use the deprecatedCGWindowListCreateImagefamily. - Deployment target: macOS 26.0+ (raised from 14.0 in 2026-07 for the Foundation Models framework). Apple Intelligence availability must still be checked at runtime — macOS 26 runs on some Intel Macs and users can keep Apple Intelligence off.
- Project generation: XcodeGen.
The
.xcodeprojis gitignored; editproject.ymland re-runxcodegen. - Module layout:
Bokashi/— the macOS app target (UI, app delegate, menubar, capture overlay). Keep this layer thin.Packages/BokashiCore/— local Swift package. Pure-Swift logic that can be unit-tested without an app host (annotation models, mask application, file IO helpers, etc.). Prefer putting code here.
- Coding style: Follow Swift API Design Guidelines. No comments unless the why is non-obvious. No emojis in code or commits.
# Generate Xcode project
xcodegen
# Build (Debug, unsigned, suitable for local dev)
xcodebuild -project Bokashi.xcodeproj -scheme Bokashi -configuration Debug build CODE_SIGNING_ALLOWED=NO
# Run BokashiCore unit tests (no app host needed)
swift test --package-path Packages/BokashiCore- Screen Recording is required for capture. Ask via
CGRequestScreenCaptureAccess()and surface a clear error if denied. - Accessibility is not required for global hotkeys when using
KeyboardShortcuts/ CarbonRegisterEventHotKey. - The app is configured as
LSUIElement(menubar-only, no Dock icon).
project.yml currently pins manual signing to the maintainer's free
Personal Team (DEVELOPMENT_TEAM + CODE_SIGN_IDENTITY SHA hash).
The hash is the maintainer's Apple Development certificate fingerprint;
it is not secret, but it only matches the maintainer's keychain.
A stable signing identity is what keeps macOS's TCC grants (Screen
Recording, Notifications, etc.) attached to the binary across rebuilds.
Without it, every Xcode build appears as a new app to TCC and silently
loses permission, forcing a tccutil reset ScreenCapture com.snaka.Bokashi
between iterations.
If you are not the maintainer and want to build locally, either:
- Replace
DEVELOPMENT_TEAMandCODE_SIGN_IDENTITYinproject.ymlwith your own Apple Development certificate hash (find withsecurity find-identity -v -p codesigning), or - Strip both keys plus
CODE_SIGN_STYLE: Manualto fall back to ad-hoc signing — the app still builds, but TCC permissions will reset on each rebuild.
| Concern | Location |
|---|---|
| Annotation data model (value types) | Packages/BokashiCore/Sources/BokashiCore/Annotation/ |
| Capture orchestration (ScreenCaptureKit wrappers) | Bokashi/Capture/ |
| Clipboard import (pasteboard image into the editor) | Bokashi/Import/ |
| Share extension (appex handing images to the app) | BokashiShare/ |
| Region-select transparent overlay | Bokashi/Capture/Overlay/ |
| Editor window and Canvas drawing | Bokashi/Editor/ |
| Tool implementations (arrow / box / ellipse / line / mosaic) | Bokashi/Editor/Tools/ |
| Export (PNG, clipboard) | Packages/BokashiCore/Sources/BokashiCore/Export/ |
| Global hotkey registration | Bokashi/Hotkey/ |
| Settings UI and persistence | Bokashi/Settings/ |
This layout is aspirational — early milestones may flatten it. Don't create empty directories ahead of need.
- Don't add cross-platform abstractions (Linux/Windows). macOS only.
- Don't propose Mac App Store distribution unless the user revisits it. Distribution is GitHub Releases (Developer ID + notarized) → Homebrew Cask.
- Don't add cloud-upload features without an explicit ask. If users need uploads, they should configure their own destinations (S3 / Imgur / etc.).
- Don't introduce heavy dependencies for the auto-mask feature (no large ML model downloads). Vision framework + regex is the path; CoreML small models are acceptable if they ship inside the app bundle.
Milestones M0 (current — repo scaffolding) through M5 (auto-detect) are
listed in docs/ROADMAP.md. Keep that file in sync when
milestones complete or scope changes.
Full procedure lives in RELEASE.md; this is the short
playbook for when "publish a new version" comes up:
- Pick the next version. Semver: feature-set bump = minor, bug fix
= patch. Default to a minor bump (e.g.
0.4.0→0.5.0) when one or more features have landed since the last tag. - Confirm the tag is free.
git ls-remote --tags originplusgh release list. If the chosen tag already exists on the remote, do not force-push or delete it — bump to the next minor instead and add a short note inCHANGELOG.mdexplaining the skip. (This exact case happened with v0.4.0; the fix was to ship as v0.5.0.) - Prep PR. Branch
chore/prep-vX.Y.Z. BumpMARKETING_VERSIONinproject.ymland add a new[X.Y.Z] - YYYY-MM-DDsection toCHANGELOG.mdwith the changes since the previous tag, grouped into Added / Changed / Fixed / Privacy / Notes. Close it out with a reference link at the bottom. Runxcodegenso the.xcodeprojstays in sync locally (the file itself is gitignored, but the run shakes out any project.yml typos). - Merge the prep PR. The user merges; the assistant does not
gh pr mergeshared-state PRs without an explicit request. - Tag and push. From
main:Thegit checkout main && git pull git tag vX.Y.Z git push origin vX.Y.Zrelease.ymlworkflow signs, notarizes, attaches a.dmgto a new GitHub Release, and updates the Homebrew cask formula insnaka/homebrew-tap. No further action. - Watch the workflow.
gh run watch(or the Actions tab). On failure, see the Troubleshooting section inRELEASE.md.