You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: AGENTS.md
+7-6Lines changed: 7 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,10 +7,11 @@ OpenClip is a macOS floating-popup utility (Swift 6, macOS 14+, AppKit + SwiftUI
7
7
The Xcode project is **generated by XcodeGen** from `project.yml`, not edited by hand. After adding/removing `.swift` files, re-run `xcodegen generate` (the scripts re-run it automatically).
@@ -20,7 +21,7 @@ All Swift is Swift 6 with `SWIFT_STRICT_CONCURRENCY: complete`. Targets (see `pr
20
21
## Architecture hard rules
21
22
22
23
-**Core is pure** — no AppKit/SwiftUI imports in `Sources/Core/`, and the boundary is enforced by concept, not just import-grepping: UI-only presentation concerns (popup sizing/timing constants, chrome-style presentation metadata) live in `Sources/OpenClip/` — e.g. `PopupMetrics` holds popup/search sizing and `AIResultCardView` renders AI results natively — while `Core/Selection/Constants.swift` keeps only domain/runtime constants (timeouts, key codes, env vars, manifest keys). Platform side-effects live in `Sources/OpenClip/` (runtimes, `ActionResultHandler`, `DefaultActionFactory`).
23
-
-**No direct `UserDefaults.standard`** in new code — route through `SettingsStore` + `SettingKey`. The only remaining raw access is the one-time `aiCloudAPIKey` migration in `AIServiceManager` (read-then-delete); don't add more. Secrets (AI API key, secret options) go to the **Keychain**, never UserDefaults.
24
+
-**No direct `UserDefaults.standard`** in new code — route through `SettingsStore` + `SettingKey`. The only remaining raw access is the one-time `aiCloudAPIKey` migration in `AIServiceManager` (read-then-delete); don't add more. Secrets (AI API key, secret options) go to `SecretStore` (`~/.openclip/secrets.json` with 0600 POSIX permissions), never UserDefaults.
24
25
-**`ActionCoordinator` is the composition root.** Managers report registry changes via `onRegister`/`onUnregister` callbacks only; nothing else touches `ActionRegistry.shared`.
25
26
-**No `switch action.id` string-matching** in UI/presentation — use `ActionChrome` / `ConfigurableAction.preferenceIconName` instead.
26
27
- Any new subprocess-spawning action must kill the child after `Constants.scriptTimeout` (30 s, the shared watchdog); shell/AppleScript/JS runtimes all join the existing `ShellProcessRunner` executor rather than spawning their own.
@@ -54,9 +55,9 @@ The **authoritative manifest / JS-bridge spec is `docs/developer-guide/AGENTS.md
54
55
`Extensions/` is a **git submodule** (the openclip-extensions catalog); `Extensions/raw/` sits inside that submodule's working tree, so scaffolded extensions get committed there.
55
56
56
57
## Tests
57
-
58
+
58
59
-`Tests/OpenClipTests/` is one flat target. Test classes that touch app singletons call `TestIsolation.reset()` in `setUp()`; store-backed tests use `MemorySettingsStore` rather than the real preferences domain.
59
-
-A few deliberate live-integration tests remain and touch real system state (restored afterward): `TextRetrieverTests`(system clipboard), `KeychainActionOptionStoreTests` (real Keychain), `ScriptActionExecutionTests`/`ActionResultHandlerTests` (real subprocesses). Don't remove them without a seam.
60
+
-All unit and integration tests are isolated with in-memory test doubles and temporary directories (including `TextRetrieverTests`and `SecretActionOptionStoreTests`), allowing the full suite to run cleanly in headless CI with zero skips.
0 commit comments