Skip to content

Commit 96c3260

Browse files
committed
docs(trellis): 记录 macOS 快捷操作契约
1 parent 796dc6d commit 96c3260

8 files changed

Lines changed: 304 additions & 0 deletions

File tree

‎.trellis/spec/infra/index.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@
1919
| [rathole template](./rathole.md) | rathole 裸二进制、PM2 管理、`.local.toml` 与公网白名单转发模板约定 | Active |
2020
| [Dev Container Templates](./devcontainer-templates.md) | VS Code Dev Container 标准模板、宿主配置挂载与 Agent CLI 复用约定 | Active |
2121
| [Hammerspoon Plugin Contract](./hammerspoon-plugins.md) | macOS Hammerspoon 插件目录、配置合并、部署 manifest 与验证契约 | Active |
22+
| [macOS Finder Quick Actions Contract](./macos-quick-actions.md) | Finder 快捷操作 workflow、Services 安装位置、通用 runner 和 AppleScript quoting 契约 | Active |
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# macOS Finder Quick Actions Contract
2+
3+
## Scenario: Finder 快捷操作通用分派器
4+
5+
### 1. Scope / Trigger
6+
7+
- Trigger: 在 `macos/quick-actions/` 下新增或修改 Finder 右键快捷操作、Automator workflow、`~/Library/Services/*.workflow` 安装脚本或动作分派逻辑。
8+
- 目标:workflow 只作为 Finder UI 入口,业务逻辑放在仓库脚本中,方便验证、复用和后续替换为 Go 二进制 runner。
9+
- 目录名说明:`~/Library/Services` 是 macOS 历史安装位置;Finder UI 可能显示为“快捷操作”或“服务”,文档应同时覆盖这两个入口名称。
10+
11+
### 2. Signatures
12+
13+
- 安装入口:
14+
15+
```zsh
16+
zsh macos/08installQuickActions.zsh [--dry-run] [--uninstall]
17+
```
18+
19+
- 通用分派器:
20+
21+
```zsh
22+
zsh macos/quick-actions/run.zsh <action-id> [path...]
23+
```
24+
25+
- 动作脚本示例:
26+
27+
```zsh
28+
zsh macos/quick-actions/fix-app-open-issue.zsh [--dry-run] <app-path>...
29+
```
30+
31+
### 3. Contracts
32+
33+
- workflow contract:
34+
- workflow 文件放在 `macos/quick-actions/<Name>.workflow/`。
35+
- 安装脚本复制 workflow 到 `~/Library/Services/<Name>.workflow`。
36+
- workflow 的 Run Shell Script 只负责调用 `run.zsh <action-id> "$@"`。
37+
- workflow 内部不要内联业务逻辑,不要直接调用具体动作脚本。
38+
- runner contract:
39+
- 第一个参数必须是 action id。
40+
- 未知 action id 返回 2,并输出可读错误。
41+
- 已知 action id 透传剩余参数到对应动作脚本。
42+
- action contract:
43+
- 动作脚本必须自行校验 Finder 传入路径。
44+
- 非目标类型输入必须安全跳过,不执行破坏性操作。
45+
- 公共函数必须带中文注释,说明功能、入参和返回值。
46+
47+
### 4. Validation & Error Matrix
48+
49+
| 条件 | 期望行为 |
50+
|------|----------|
51+
| `run.zsh` 缺少 action id | 返回 2,输出 usage |
52+
| `run.zsh` 收到未知 action id | 返回 2,输出未知动作和 usage |
53+
| workflow 未安装 | `06verifyInstall.zsh --step quick-actions` 失败并提示运行安装脚本 |
54+
| workflow 命令未指向 `run.zsh` 和 action id | quick-actions 验证失败 |
55+
| 传入非 `.app` 到 `fix-app-open-issue` | 安全跳过,返回成功 |
56+
| 传入 `.app` 到 `fix-app-open-issue` | 输出 `spctl`、`codesign`、quarantine 状态,再按安全边界处理 |
57+
58+
### 5. Good/Base/Bad Cases
59+
60+
- Good: `workflow -> run.zsh fix-app-open-issue "$@" -> fix-app-open-issue.zsh "$@"`,业务逻辑可独立命令行验证。
61+
- Base: 安装脚本支持 `--dry-run` 和重复安装,不删除用户其它 Services。
62+
- Bad: 每个 workflow 都复制一段不同的 AppleScript、shell quote 和业务逻辑,后续修 bug 时需要逐个同步。
63+
64+
### 6. Tests Required
65+
66+
- 静态检查:
67+
68+
```zsh
69+
zsh -n macos/08installQuickActions.zsh macos/quick-actions/run.zsh macos/quick-actions/*.zsh
70+
plutil -lint "macos/quick-actions/Fix App Open Issue.workflow/Contents/Info.plist"
71+
plutil -lint "macos/quick-actions/Fix App Open Issue.workflow/Contents/document.wflow"
72+
```
73+
74+
- 行为检查:
75+
76+
```zsh
77+
zsh macos/08installQuickActions.zsh --dry-run
78+
zsh macos/08installQuickActions.zsh
79+
zsh macos/06verifyInstall.zsh --step quick-actions
80+
zsh macos/quick-actions/run.zsh fix-app-open-issue --dry-run /tmp/not-an-app
81+
```
82+
83+
- AppleScript quoting 检查:
84+
85+
```zsh
86+
osascript - "/tmp/a path/run.zsh" "fix-app-open-issue" "/tmp/My App.app" <<'APPLESCRIPT'
87+
on run argv
88+
if (count of argv) < 2 then return
89+
set commandText to "/bin/zsh " & quoted form of (item 1 of argv) & " " & quoted form of (item 2 of argv)
90+
repeat with i from 3 to count of argv
91+
set commandText to commandText & " " & quoted form of (item i of argv)
92+
end repeat
93+
return commandText
94+
end run
95+
APPLESCRIPT
96+
```
97+
98+
### 7. Wrong vs Correct
99+
100+
#### Wrong
101+
102+
```applescript
103+
on shellQuote(value)
104+
set AppleScript's text item delimiters to "'\\''"
105+
...
106+
end shellQuote
107+
```
108+
109+
这种手写 quote 容易在 Automator 的 Run Shell Script 字符串中触发 AppleScript 语法错误,例如“预期是 `"`,却找到未知的记号”。
110+
111+
#### Correct
112+
113+
```applescript
114+
set commandText to "/bin/zsh " & quoted form of (item 1 of argv) & " " & quoted form of (item 2 of argv)
115+
repeat with i from 3 to count of argv
116+
set commandText to commandText & " " & quoted form of (item i of argv)
117+
end repeat
118+
```
119+
120+
AppleScript 自带 `quoted form of`,应优先使用它生成 shell 参数;workflow 内只拼 runner 和 action id,复杂逻辑放到仓库脚本中。
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# macOS 应用打不开右键修复快捷指令设计
2+
3+
## Problem
4+
5+
下载或解压得到的 `.app` 有时被 macOS Gatekeeper/quarantine 拦截,用户需要在 Finder 里选中应用后用右键动作完成诊断、清除隔离属性并尝试打开,避免每次手动复制终端命令。
6+
7+
## Architecture
8+
9+
- 新增 `macos/quick-actions/` 作为 macOS Finder 右键动作资产目录。
10+
- 新增可安装的 Finder Quick Action/Automator workflow 项目,例如 `Fix App Open Issue.workflow`。
11+
- 新增安装脚本,例如 `macos/08installQuickActions.zsh`,负责把仓库内 workflow 批量同步到 `~/Library/Services/`。
12+
- workflow 内部调用仓库提供的通用分派器 `macos/quick-actions/run.zsh`,传入动作 ID(例如 `fix-app-open-issue`)和 Finder 选中路径列表。
13+
- `macos/INSTALL.md` 新增安装步骤,说明安装、验证、使用和安全边界。
14+
15+
## Technical Choice
16+
17+
采用 Automator workflow / Finder Quick Action 作为第一版交付格式,而不是只提交 Apple Shortcuts `.shortcut` 文件。
18+
19+
依据:
20+
21+
- Apple Shortcuts 文档支持从 Finder Quick Actions 或 Services 调用快捷指令,也支持 `shortcuts run -i` 传入文件路径。
22+
- 当前本机 `shortcuts` CLI 暴露 `run/list/view/sign`,没有显示稳定的静默导入子命令;这不利于仓库内批量安装。
23+
- `~/Library/Services/*.workflow` 是当前用户 Finder 快捷操作/服务的可复制安装位置,更适合脚本化部署和验证;这里的 `Services` 是 macOS 历史目录名,Finder UI 仍可能显示为“快捷操作”。
24+
25+
## Data Flow
26+
27+
1. 用户在 Finder 中选择一个或多个项目。
28+
2. 用户右键选择 Quick Actions/Services 中的“处理 macOS 应用打不开”动作。
29+
3. workflow 把动作 ID 和选中路径作为参数传给 `run.zsh`。
30+
4. `run.zsh` 按动作 ID 分派到具体动作脚本,例如 `fix-app-open-issue.zsh`。
31+
5. 动作脚本逐个路径处理:
32+
- 输出当前处理路径。
33+
- 非目录或非 `.app` 路径直接跳过。
34+
- 执行 `/usr/sbin/spctl -a -vv "$app"` 输出 Gatekeeper 诊断。
35+
- 执行 `/usr/bin/codesign --verify --deep --strict --verbose=2 "$app"` 输出签名诊断。
36+
- 执行 `/usr/bin/xattr -dr com.apple.quarantine "$app"` 清除隔离属性。
37+
- 执行 `/usr/bin/open "$app"` 尝试打开应用。
38+
39+
## Safety Boundary
40+
41+
- 默认自动修复,但只处理 Finder 传入且后缀为 `.app` 的目录。
42+
- 对非 `.app` 输入只输出跳过原因,不执行 `xattr -dr`。
43+
- 不全局关闭 Gatekeeper,不修改系统安全策略。
44+
- 文档明确:只对可信来源应用使用;诊断输出用于辅助判断签名和来源是否异常。
45+
- workflow 内部 AppleScript 使用 `quoted form of` 生成 Terminal 命令参数,避免手写 shell quote 在 AppleScript 字符串中触发语法错误。
46+
47+
## Compatibility
48+
49+
- 目标平台是 macOS 当前用户环境。
50+
- 安装脚本创建 `~/Library/Services`,并把 workflow 复制或同步进去。
51+
- 如果 Finder 没有立即显示新动作,文档提示重新打开 Finder 窗口或重启 Finder。
52+
- 验证脚本可以检查 workflow 是否已存在于 `~/Library/Services`,但不强制验证右键菜单 UI 是否已刷新。
53+
54+
## Rollback
55+
56+
- 删除 `~/Library/Services/<workflow-name>.workflow` 即可移除右键动作。
57+
- 仓库安装脚本可提供 `--uninstall` 或在文档中给出删除命令;第一版优先实现 `--dry-run` 和 idempotent install,`--uninstall` 视实现复杂度决定。
58+
59+
## Trade-offs
60+
61+
- Automator workflow 比纯 `.shortcut` 更容易批量安装和验证,但 UI 上可能显示在 Services/Quick Actions,而不是 Shortcuts App 的收藏列表。
62+
- 默认自动清除 quarantine 操作效率高,但要求用户只对可信来源应用使用;因此必须保留诊断输出和文档警示。
63+
- 通用分派器让后续新增 Finder 快捷操作时只需要增加 action id 和处理脚本,workflow 继续保持薄入口;如果未来确实需要常驻服务,可以优先把 `run.zsh` 的分派契约迁移到 Go 二进制。
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"_example": "Fill with {\"file\": \"<path>\", \"reason\": \"<why>\"}. Put spec/research files only — no code paths. Run `python3 .trellis/scripts/get_context.py --mode packages` to list available specs. Delete this line once real entries are added."}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# macOS 应用打不开右键修复快捷指令执行计划
2+
3+
## Implementation Checklist
4+
5+
- [x] 新增 `macos/quick-actions/` 目录。
6+
- [x] 新增通用分派器 `macos/quick-actions/run.zsh`,通过动作 ID 触发具体快捷操作。
7+
- [x] 新增处理脚本 `macos/quick-actions/fix-app-open-issue.zsh`,实现批量 `.app` 诊断、清除 quarantine 和打开。
8+
- [x] 新增 Finder Quick Action/Automator workflow 资产,调用处理脚本并接收 Finder 选中项。
9+
- [x] 新增批量安装脚本 `macos/08installQuickActions.zsh`,把 workflow 安装到 `~/Library/Services`。
10+
- [x] 更新 `macos/INSTALL.md`,加入安装步骤、验证方式、使用方式和安全边界。
11+
- [x] 按需更新 `macos/06verifyInstall.zsh`,增加 quick actions 的可发现性检查。
12+
- [x] 本地验证安装脚本 dry-run、真实安装路径和处理脚本的非 `.app` 跳过逻辑。
13+
14+
## Validation Commands
15+
16+
```zsh
17+
zsh macos/08installQuickActions.zsh --dry-run
18+
zsh macos/08installQuickActions.zsh
19+
test -d "$HOME/Library/Services/Fix App Open Issue.workflow"
20+
zsh macos/quick-actions/fix-app-open-issue.zsh /tmp/not-an-app
21+
pnpm qa
22+
```
23+
24+
若实现改动涉及 PowerShell 或 `profile/**`,追加:
25+
26+
```zsh
27+
pnpm test:pwsh:all
28+
```
29+
30+
## Risky Files
31+
32+
- `macos/06verifyInstall.zsh`:已有多步骤验证逻辑,若修改需保持其他步骤行为不变。
33+
- `macos/INSTALL.md`:新增步骤编号可能影响现有文档引用,需要保持流程清楚。
34+
- `~/Library/Services`:安装脚本写入用户本机服务目录,应支持 dry-run,并避免删除用户已有服务。
35+
36+
## Review Gate
37+
38+
实现前需要用户确认本规划进入 Phase 2。启动后执行:
39+
40+
```zsh
41+
python3 ./.trellis/scripts/task.py start 06-23-macos-app-open-fix-shortcut
42+
```
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# macOS 应用打不开右键修复快捷指令
2+
3+
## Goal
4+
5+
在 macOS 安装流程中新增一个可批量安装的 Finder 右键快捷指令/服务,用于处理下载后的 `.app` 无法打开、疑似 Gatekeeper 或 quarantine 阻断的问题。用户可以在 Finder 中选中一个或多个 `.app` 后右键执行,工具先输出诊断信息,再按设计边界处理隔离属性并尝试打开应用。
6+
7+
用户价值:把一次性终端修复命令沉淀成可复用的 macOS 右键动作,减少每次遇到“打不开软件”时重复查命令、猜权限原因的成本。
8+
9+
## Confirmed Facts
10+
11+
- 现有 macOS 安装流程位于 `macos/`,包含 `04installApps.ps1`、`05deployHammerspoon.sh`、`06verifyInstall.zsh`、`07configureLoginItems.zsh`,目前没有 Finder 右键快捷指令/Quick Action 的目录或安装步骤。
12+
- `macos/04installApps.ps1` 通过 Homebrew 安装 `supportOs` 包含 `macOS` 且 `tag` 包含 `macbook` 的应用;这次需求不是新增 Homebrew app,而是新增可部署的本机自动化资产。
13+
- `macos/INSTALL.md` 已按安装步骤记录执行方式、验证方式和失败处理;新增快捷指令安装项应进入这个文档的 macOS 流程。
14+
- 既有记忆中的 `.app` 打不开处理顺序是:先查 `xattr` 是否有 `com.apple.quarantine`,再跑 `spctl -a -vv`,再跑 `codesign --verify --deep --strict --verbose=2` 或类似签名检查,最后才对可信 app 执行 `xattr -dr com.apple.quarantine`。
15+
- 用户提供的参考脚本支持批量处理参数中的 `.app`,对非 `.app` 跳过,并执行 Gatekeeper 检查、签名检查、移除隔离属性和 `open`。
16+
17+
## Requirements
18+
19+
- 在 `macos/` 下新增一个专门存放快捷指令/右键动作资产的目录,命名应能清楚表达用途。
20+
- 提供一个 Finder 右键可调用的项目,用于处理选中的一个或多个 `.app`。
21+
- 提供批量安装入口,将该右键动作安装到当前用户可用的位置。
22+
- 右键动作应支持多选 `.app`,对非目录或非 `.app` 输入安全跳过并输出说明。
23+
- 动作执行时应输出至少三类诊断信息:Gatekeeper 检查、签名检查、隔离属性处理结果。
24+
- 文档应说明安装方式、使用方式、适用边界和风险边界。
25+
- 实现应遵循仓库现有 macOS zsh 脚本风格:`set -euo pipefail`、中文公共接口注释、可读日志、必要时支持 dry-run 或可验证输出。
26+
27+
## Acceptance Criteria
28+
29+
- [x] 仓库存在 `macos/` 下的快捷指令/右键动作目录,并包含“处理 macOS 应用打不开”的项目资产。
30+
- [x] 有一个批量安装命令可把该项目安装到当前用户 Finder 右键动作可发现的位置。
31+
- [x] Finder 选中一个或多个 `.app` 后右键可触发该动作;脚本能逐个处理传入路径。
32+
- [x] 非 `.app` 输入不会执行 quarantine 清理,并会给出跳过原因。
33+
- [x] 对 `.app` 输入会执行 `spctl` 和 `codesign` 诊断,并按最终安全策略处理 `com.apple.quarantine`。
34+
- [x] `macos/INSTALL.md` 记录新增步骤的执行方式、验证方式、失败处理和权限/安全说明。
35+
- [x] 完成实现后执行根目录 `pnpm qa`;若改动涉及 pwsh 内容,额外执行 `pnpm test:pwsh:all`。
36+
37+
## Out of Scope
38+
39+
- 不全局关闭 Gatekeeper。
40+
- 不修改 macOS 系统安全策略。
41+
- 不把该动作注册为所有文件类型的默认打开方式。
42+
- 不处理非 `.app` bundle 的修复,例如 `.pkg`、`.dmg`、命令行二进制或浏览器下载配置。
43+
44+
## Open Questions
45+
46+
- 已决策:右键动作默认自动修复,仅处理传入的 `.app`;先输出 Gatekeeper 和签名诊断,再清除 `com.apple.quarantine` 并尝试打开应用。文档必须明确只对可信来源应用使用。
47+
48+
## Notes
49+
50+
- 这项任务跨安装脚本、Finder 右键动作资产、文档和验证路径,后续进入实现前应补 `design.md` 与 `implement.md`。
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"id": "macos-app-open-fix-shortcut",
3+
"name": "macos-app-open-fix-shortcut",
4+
"title": "macOS 应用打不开右键修复快捷指令",
5+
"description": "",
6+
"status": "in_progress",
7+
"dev_type": null,
8+
"scope": null,
9+
"package": null,
10+
"priority": "P2",
11+
"creator": "mudssky",
12+
"assignee": "mudssky",
13+
"createdAt": "2026-06-23",
14+
"completedAt": null,
15+
"branch": null,
16+
"base_branch": "master",
17+
"worktree_path": null,
18+
"commit": null,
19+
"pr_url": null,
20+
"subtasks": [],
21+
"children": [],
22+
"parent": null,
23+
"relatedFiles": [],
24+
"notes": "",
25+
"meta": {}
26+
}

0 commit comments

Comments
 (0)