harness-gateway 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. harness_gateway-0.7.0/.codebuddy/commands/ship.md +47 -0
  2. harness_gateway-0.7.0/.codebuddy/rules/python-code-quality.mdc +113 -0
  3. harness_gateway-0.7.0/.codebuddy/skills/pypi-publish/SKILL.md +307 -0
  4. harness_gateway-0.7.0/.env.example +55 -0
  5. harness_gateway-0.7.0/.github/pull_request_template.md +23 -0
  6. harness_gateway-0.7.0/.github/workflows/ci.yml +38 -0
  7. harness_gateway-0.7.0/.github/workflows/codeql.yml +30 -0
  8. harness_gateway-0.7.0/.github/workflows/release.yml +101 -0
  9. harness_gateway-0.7.0/.gitignore +42 -0
  10. harness_gateway-0.7.0/.pre-commit-config.yaml +7 -0
  11. harness_gateway-0.7.0/AGENTS.md +596 -0
  12. harness_gateway-0.7.0/CHANGELOG.md +36 -0
  13. harness_gateway-0.7.0/CONTRIBUTING.md +81 -0
  14. harness_gateway-0.7.0/LICENSE +21 -0
  15. harness_gateway-0.7.0/Makefile +168 -0
  16. harness_gateway-0.7.0/PKG-INFO +491 -0
  17. harness_gateway-0.7.0/README.md +434 -0
  18. harness_gateway-0.7.0/README_CN.md +443 -0
  19. harness_gateway-0.7.0/SECURITY.md +23 -0
  20. harness_gateway-0.7.0/assets/images/banner.jpeg +0 -0
  21. harness_gateway-0.7.0/docs/superpowers/specs/2026-05-26-engineering-enhancement-design.md +345 -0
  22. harness_gateway-0.7.0/docs/superpowers/specs/2026-05-27-harness-agent-v2-design.md +97 -0
  23. harness_gateway-0.7.0/examples/advanced/feishu_advanced.py +61 -0
  24. harness_gateway-0.7.0/examples/advanced/push_scheduler.py +65 -0
  25. harness_gateway-0.7.0/examples/advanced/qq_advanced.py +76 -0
  26. harness_gateway-0.7.0/examples/all_channels.py +103 -0
  27. harness_gateway-0.7.0/examples/backends/__init__.py +1 -0
  28. harness_gateway-0.7.0/examples/backends/harness_agent.py +631 -0
  29. harness_gateway-0.7.0/examples/feishu_bot.py +59 -0
  30. harness_gateway-0.7.0/examples/mqtt_bot.py +77 -0
  31. harness_gateway-0.7.0/examples/qq_bot.py +59 -0
  32. harness_gateway-0.7.0/examples/telegram_bot.py +41 -0
  33. harness_gateway-0.7.0/examples/wecom_bot.py +60 -0
  34. harness_gateway-0.7.0/examples/weixin_bot.py +67 -0
  35. harness_gateway-0.7.0/examples/xiaoyi_bot.py +41 -0
  36. harness_gateway-0.7.0/pyproject.toml +147 -0
  37. harness_gateway-0.7.0/src/harness_gateway/__init__.py +65 -0
  38. harness_gateway-0.7.0/src/harness_gateway/channel.py +960 -0
  39. harness_gateway-0.7.0/src/harness_gateway/channels/__init__.py +103 -0
  40. harness_gateway-0.7.0/src/harness_gateway/channels/dingtalk.py +727 -0
  41. harness_gateway-0.7.0/src/harness_gateway/channels/feishu.py +842 -0
  42. harness_gateway-0.7.0/src/harness_gateway/channels/mqtt.py +283 -0
  43. harness_gateway-0.7.0/src/harness_gateway/channels/qq.py +1195 -0
  44. harness_gateway-0.7.0/src/harness_gateway/channels/telegram/__init__.py +5 -0
  45. harness_gateway-0.7.0/src/harness_gateway/channels/telegram/channel.py +317 -0
  46. harness_gateway-0.7.0/src/harness_gateway/channels/telegram/format_html.py +75 -0
  47. harness_gateway-0.7.0/src/harness_gateway/channels/wecom.py +532 -0
  48. harness_gateway-0.7.0/src/harness_gateway/channels/weixin/__init__.py +26 -0
  49. harness_gateway-0.7.0/src/harness_gateway/channels/weixin/channel.py +416 -0
  50. harness_gateway-0.7.0/src/harness_gateway/channels/weixin/login_qr.py +243 -0
  51. harness_gateway-0.7.0/src/harness_gateway/channels/xiaoyi/__init__.py +5 -0
  52. harness_gateway-0.7.0/src/harness_gateway/channels/xiaoyi/auth.py +26 -0
  53. harness_gateway-0.7.0/src/harness_gateway/channels/xiaoyi/channel.py +720 -0
  54. harness_gateway-0.7.0/src/harness_gateway/channels/xiaoyi/constants.py +12 -0
  55. harness_gateway-0.7.0/src/harness_gateway/channels/yuanbao.py +243 -0
  56. harness_gateway-0.7.0/src/harness_gateway/constraints.py +283 -0
  57. harness_gateway-0.7.0/src/harness_gateway/manager.py +831 -0
  58. harness_gateway-0.7.0/src/harness_gateway/media.py +75 -0
  59. harness_gateway-0.7.0/src/harness_gateway/models.py +309 -0
  60. harness_gateway-0.7.0/src/harness_gateway/py.typed +0 -0
  61. harness_gateway-0.7.0/src/harness_gateway/utils.py +158 -0
  62. harness_gateway-0.7.0/tests/__init__.py +0 -0
  63. harness_gateway-0.7.0/tests/channels/__init__.py +0 -0
  64. harness_gateway-0.7.0/tests/channels/test_dingtalk.py +130 -0
  65. harness_gateway-0.7.0/tests/channels/test_feishu.py +441 -0
  66. harness_gateway-0.7.0/tests/channels/test_mqtt.py +33 -0
  67. harness_gateway-0.7.0/tests/channels/test_qq.py +916 -0
  68. harness_gateway-0.7.0/tests/channels/test_send_local_media.py +270 -0
  69. harness_gateway-0.7.0/tests/channels/test_telegram.py +41 -0
  70. harness_gateway-0.7.0/tests/channels/test_typing_indicator.py +279 -0
  71. harness_gateway-0.7.0/tests/channels/test_wecom.py +351 -0
  72. harness_gateway-0.7.0/tests/channels/test_weixin.py +266 -0
  73. harness_gateway-0.7.0/tests/channels/test_xiaoyi.py +44 -0
  74. harness_gateway-0.7.0/tests/conftest.py +18 -0
  75. harness_gateway-0.7.0/tests/test_channel.py +701 -0
  76. harness_gateway-0.7.0/tests/test_constraints.py +461 -0
  77. harness_gateway-0.7.0/tests/test_harness_agent_backend.py +758 -0
  78. harness_gateway-0.7.0/tests/test_manager.py +485 -0
  79. harness_gateway-0.7.0/tests/test_media.py +59 -0
  80. harness_gateway-0.7.0/tests/test_media_integration.py +153 -0
  81. harness_gateway-0.7.0/tests/test_models.py +145 -0
  82. harness_gateway-0.7.0/tests/test_utils.py +191 -0
  83. harness_gateway-0.7.0/uv.lock +3570 -0
@@ -0,0 +1,47 @@
1
+ Analyze the current git changes, run code quality checks, create a well-structured commit, and push to the remote. Follow these steps precisely:
2
+
3
+ ## Step 1: Analyze Changes
4
+
5
+ Run `git status` and `git diff --stat` to understand the scope of changes. If there are unstaged changes, stage them with `git add -A`.
6
+
7
+ If there are no changes to commit, inform me and stop.
8
+
9
+ ## Step 2: Code Quality Checks (Python only)
10
+
11
+ If any staged changes include `.py` files, run the following checks **before committing**:
12
+
13
+ ```bash
14
+ ruff check . --fix
15
+ ruff format .
16
+ ```
17
+
18
+ - If `ruff check --fix` auto-fixes issues, re-stage the affected files with `git add -A`.
19
+ - If `ruff check` still reports errors after `--fix` (i.e., errors that require manual intervention), **stop and report the errors**. Do not proceed with the commit until all errors are resolved.
20
+ - If there are no `.py` files in the staged changes, skip this step.
21
+
22
+ ## Step 3: Generate Commit Message
23
+
24
+ Run `git diff --cached` to inspect the staged changes in detail.
25
+
26
+ Write a **Conventional Commits** message following this format:
27
+
28
+ ```
29
+ <type>(<scope>): <concise summary>
30
+
31
+ <optional body — bullet points explaining key changes>
32
+ ```
33
+
34
+ Rules for the commit message:
35
+ - **type**: one of `feat`, `fix`, `refactor`, `chore`, `docs`, `style`, `perf`, `test`, `build`, `ci`
36
+ - **scope**: the primary module or area affected (e.g., `agent`, `dashboard`, `memory`, `config`)
37
+ - **summary**: imperative mood, lowercase, no period, ≤ 72 characters
38
+ - **body**: include only when there are multiple logical changes; use bullet points
39
+ - The commit message (including body) must be in **English**
40
+
41
+ ## Step 4: Commit and Push
42
+
43
+ 1. Create the commit with the generated message.
44
+ 2. Push to the remote tracking branch (usually `origin/<current-branch>`).
45
+ 3. Show me the final `git log --oneline -1` and confirm success.
46
+
47
+ $ARGUMENTS
@@ -0,0 +1,113 @@
1
+ ---
2
+ description:
3
+ alwaysApply: true
4
+ enabled: true
5
+ updatedAt: 2026-03-17T06:54:46.604Z
6
+ provider:
7
+ ---
8
+
9
+ # Python Code Quality Standards — LightClaw
10
+
11
+ ## 1. Pre-commit: ruff check & ruff format
12
+
13
+ Every Python change **must** pass the following before commit:
14
+
15
+ ```bash
16
+ ruff check . --fix # lint + auto-fix
17
+ ruff format . # deterministic formatting
18
+ ```
19
+
20
+ - If `ruff check` reports errors that `--fix` cannot resolve, fix them **manually** before proceeding.
21
+ - Never suppress a warning with `# noqa` unless there is a clear, documented reason (e.g., an import purely for availability detection).
22
+ - When generating or editing Python code, ensure the result is **ruff-clean** — run `ruff check` mentally against your output.
23
+
24
+ ## 2. Modern Type Annotations (PEP 585 / PEP 604)
25
+
26
+ ### Banned legacy aliases
27
+
28
+ | ❌ Legacy (do NOT use) | ✅ Modern replacement |
29
+ |------------------------|----------------------|
30
+ | `Optional[X]` | `X \| None` |
31
+ | `Union[X, Y]` | `X \| Y` |
32
+ | `Dict[K, V]` | `dict[K, V]` |
33
+ | `List[T]` | `list[T]` |
34
+ | `Tuple[T, ...]` | `tuple[T, ...]` |
35
+ | `Set[T]` | `set[T]` |
36
+ | `FrozenSet[T]` | `frozenset[T]` |
37
+ | `Type[T]` | `type[T]` |
38
+ | `Any` | Avoid — prefer `object`, `Protocol`, `TypedDict`, or a concrete union |
39
+
40
+ ### Rules
41
+
42
+ - **Always** use lowercase built-in generics: `list[str]`, `dict[str, int]`, `tuple[int, ...]`.
43
+ - **Always** use `X | None` instead of `Optional[X]`.
44
+ - Add `from __future__ import annotations` when forward references are needed, but prefer it in all new files for consistency.
45
+ - `typing.TYPE_CHECKING` is fine for import-cycle breaking; keep runtime imports lean.
46
+ - If `Any` is truly unavoidable, add a brief comment explaining why.
47
+
48
+ ## 3. Comments & Docstrings — English Only
49
+
50
+ ### Comments
51
+
52
+ - **All comments must be in English.** No Chinese in `#` comments or inline remarks.
53
+ - Keep comments concise: explain *why*, not *what* (the code shows *what*).
54
+ - Use `# TODO(author): description` / `# FIXME(author): description` for action items.
55
+ - Avoid redundant comments that merely restate the code:
56
+ ```python
57
+ # Bad — restates the code
58
+ # Increment counter by one
59
+ counter += 1
60
+
61
+ # Good — explains intent
62
+ # Retry once more before giving up
63
+ counter += 1
64
+ ```
65
+
66
+ ### Docstrings
67
+
68
+ - Use triple double-quotes `"""` exclusively.
69
+ - First line: short imperative summary (≤ 79 chars), ending with a period.
70
+ - Blank line, then detailed description if needed.
71
+ - Document parameters, return values, and raised exceptions for public APIs.
72
+ - Prefer reStructuredText or Google-style format — be consistent within a module.
73
+ - Docstrings must also be in **English**.
74
+
75
+ ## 4. Import Hygiene
76
+
77
+ - One import per line; no `import os, sys`.
78
+ - Order: stdlib → third-party → local, separated by blank lines, sorted alphabetically within each group.
79
+ - Never use `from module import *`.
80
+ - Remove unused imports immediately — ruff rule `F401` will catch these.
81
+ - Place `from __future__ import annotations` before all other imports.
82
+
83
+ ## 5. PEP 8 Essentials (enforced by ruff)
84
+
85
+ - **Indentation**: 4 spaces, no tabs.
86
+ - **Line length**: 120 characters max (matches project `.editorconfig`).
87
+ - **Trailing whitespace**: forbidden.
88
+ - **Blank lines**: 2 between top-level definitions, 1 between methods.
89
+ - **File endings**: exactly one trailing newline, LF only.
90
+ - **Variable naming**: `snake_case` for functions/variables, `PascalCase` for classes, `UPPER_SNAKE` for constants.
91
+ - **Avoid ambiguous names**: no single-char `l`, `O`, `I` (ruff `E741`).
92
+
93
+ ## 6. Common Pitfalls to Avoid
94
+
95
+ | Rule | Example |
96
+ |------|---------|
97
+ | No mutable default args | `def f(items=None):` not `def f(items=[]):` |
98
+ | No bare `except:` | Always catch a specific exception |
99
+ | Use `is not` instead of `not ... is` | `if x is not None:` |
100
+ | Sequence truthiness | `if seq:` not `if len(seq):` |
101
+ | Use `with` for resources | Files, sockets, locks |
102
+ | No `lambda` assignment | Use `def` instead of `fn = lambda x: ...` |
103
+ | No string concat in loops | Use `"".join(parts)` |
104
+
105
+ ## 7. Quick Checklist Before Commit
106
+
107
+ - [ ] `ruff check . --fix` — zero errors
108
+ - [ ] `ruff format .` — no diff
109
+ - [ ] No `Optional`, `Dict`, `List`, `Any` in new/modified code
110
+ - [ ] All comments and docstrings are in English
111
+ - [ ] No unused imports or variables
112
+ - [ ] Type hints on all new public functions
113
+ - [ ] `from __future__ import annotations` in new files
@@ -0,0 +1,307 @@
1
+ ---
2
+ name: publish
3
+ description: 当需要发布 Python 包到 PyPI 时使用 — 创建 release 分支、升版本号、更新 CHANGELOG、发布到 PyPI、打 tag、并创建合并请求到 main
4
+ ---
5
+
6
+ # Publish
7
+
8
+ 自动化 Python 包的完整发布流程。
9
+
10
+ **开始时宣告:** "正在使用 publish 技能发布版本 {VERSION}。"
11
+
12
+ ## 配置项
13
+
14
+ 以下配置有默认值,可在项目的 `.codebuddy/skills/publish/SKILL.md` 中覆盖。
15
+
16
+ | 配置项 | 默认值 | 说明 |
17
+ |--------|--------|------|
18
+ | `PUBLISH_CMD` | 自动检测(见步骤 5) | 发布到 PyPI 的命令 |
19
+ | `CHANGELOG_FILE` | `CHANGELOG.md` | 相对于仓库根目录的路径,文件不存在则跳过 |
20
+ | `VERSION_FILE` | `pyproject.toml` | 包含版本号的文件 |
21
+ | `VERSION_PATTERN` | `^\s*version\s*=\s*"[^"]+"` | 匹配版本行的正则表达式 |
22
+ | `TAG_PREFIX` | ``(空) | Git tag 前缀,设为 `v` 则生成 `v0.1.14` 风格标签 |
23
+ | `REMOTE` | `origin` | Git 远程仓库名 |
24
+ | `TARGET_BRANCH` | `main` | 合并请求的目标分支 |
25
+ | `RELEASE_BRANCH_PREFIX` | `release/` | release 分支名前缀 |
26
+
27
+ ## 调用方式
28
+
29
+ ```
30
+ /publish 0.1.14
31
+ ```
32
+
33
+ 目标版本号是唯一必填参数,其余均从配置或自动检测获取。
34
+
35
+ ## 发布流程
36
+
37
+ ### 步骤 1 — 读取配置并确认版本
38
+
39
+ 1. 获取仓库根目录:`git rev-parse --show-toplevel`
40
+ 2. 从 `VERSION_FILE` 读取当前版本:
41
+ ```bash
42
+ grep -E '^\s*version\s*=\s*"[^"]+"' pyproject.toml
43
+ ```
44
+ 3. 检查未提交的更改:
45
+ ```bash
46
+ git status --short
47
+ ```
48
+ 如果存在未提交的更改,它们将被包含在步骤 4 的发布提交中。这是有意为之 — 所有待处理的工作随版本一起发布。
49
+ 4. 查找最近的 git tag:
50
+ ```bash
51
+ git tag --sort=-creatordate | head -1
52
+ ```
53
+ 如果没有 tag,视为首次发布(在步骤 3 中使用从仓库初始到 HEAD 的所有提交)。
54
+ 5. 展示确认信息:
55
+
56
+ ```
57
+ 当前版本 (pyproject.toml): X.Y.Z
58
+ 目标版本: A.B.C
59
+ 上次发布 tag: X.Y.Z (YYYY-MM-DD)
60
+ Release 分支: release/A.B.C
61
+ 目标分支 (MR): main
62
+
63
+ 确认发布 X.Y.Z → A.B.C?[y/N]
64
+ ```
65
+
66
+ 如果用户未输入 `y` 确认,立即中止。
67
+
68
+ ### 步骤 2 — 创建 release 分支
69
+
70
+ 从当前 HEAD 创建并切换到新的 release 分支:
71
+
72
+ ```bash
73
+ git checkout -b {RELEASE_BRANCH_PREFIX}{version}
74
+ ```
75
+
76
+ 如果分支已存在,中止:
77
+ ```
78
+ ✗ 分支 {RELEASE_BRANCH_PREFIX}{version} 已存在。
79
+ 请手动删除:git branch -D {RELEASE_BRANCH_PREFIX}{version}
80
+ 然后重新运行 /publish。
81
+ ```
82
+
83
+ ### 步骤 3 — 分析变更并生成 CHANGELOG 草稿
84
+
85
+ 1. 获取上次 tag 以来的提交:
86
+ ```bash
87
+ git log {last_tag}..HEAD --oneline
88
+ # 首次发布时:
89
+ git log --oneline
90
+ ```
91
+
92
+ 2. 如果没有找到提交:
93
+ ```
94
+ ⚠ 自上次发布 tag ({last_tag}) 以来没有新提交。
95
+ 是否继续?[y/N]
96
+ ```
97
+ 用户未确认则中止。
98
+
99
+ 3. 按提交前缀分类,生成 Keep a Changelog 格式的条目。
100
+
101
+ **CHANGELOG 内容必须用中文书写。** 将每个提交总结为简洁的中文要点 — 不要逐字翻译提交信息。适当合并相关提交。
102
+
103
+ 分类规则:
104
+ - 以 `feat:` 或 `feat(` 开头 → **新增**
105
+ - 以 `fix:` 或 `fix(` 开头 → **修复**
106
+ - 以 `refactor:` 或 `perf:` 开头 → **变更**
107
+ - 包含 `!:` 或提交正文含 `BREAKING CHANGE:` → **变更**,加 `**Breaking:**` 前缀
108
+ - 以 `docs:` 开头 → **变更**
109
+ - 以 `chore:`、`test:`、`ci:` 开头 → 忽略(基础设施噪音)
110
+ - 其他所有提交 → **变更**
111
+ - 移除的功能 → **移除**
112
+ - 安全修复 → **安全**
113
+
114
+ 输出格式:
115
+ ```markdown
116
+ ## [A.B.C] - YYYY-MM-DD
117
+
118
+ ### 新增
119
+ - 中文描述新增功能
120
+
121
+ ### 修复
122
+ - 中文描述修复内容
123
+
124
+ ### 变更
125
+ - 中文描述行为变更
126
+
127
+ ### 移除
128
+ - 中文描述移除内容
129
+
130
+ ### 安全
131
+ - 中文描述安全修复
132
+ ```
133
+ 省略空的分类。日期使用 ISO 8601 格式(当天日期)。日期行与第一个分类之间、各分类之间保留空行。
134
+
135
+ 4. 向用户展示草稿并请求确认:
136
+ ```
137
+ CHANGELOG 草稿:
138
+
139
+ {draft}
140
+
141
+ 添加到 CHANGELOG.md?[y/N/edit]
142
+ ```
143
+ - `y` → 继续
144
+ - `n` → 中止
145
+ - `edit` 或其他反馈 → 询问用户:"需要什么修改?" — 等待回复后重新生成,再次展示确认。循环直到 `y` 或 `n`。
146
+
147
+ 5. 如果 `CHANGELOG_FILE` 不存在,跳过此步骤(无需警告)。
148
+
149
+ ### 步骤 4 — 更新文件、提交并推送 release 分支
150
+
151
+ 按顺序执行:
152
+
153
+ **4a. 更新 CHANGELOG:**
154
+
155
+ 在 `CHANGELOG_FILE` 中找到 `## [Unreleased]` 标题,在其后插入新版本条目(保持 `[Unreleased]` 为空):
156
+
157
+ ```markdown
158
+ ## [Unreleased]
159
+
160
+ ## [A.B.C] - YYYY-MM-DD
161
+ ### 新增
162
+ - ...
163
+ ```
164
+
165
+ 如果 `## [Unreleased]` 标题不存在,在 `# Changelog` 标题行之后插入新条目(若无标题则插入到文件顶部)。
166
+
167
+ **4b. 升级 VERSION_FILE 中的版本号:**
168
+
169
+ 替换 `VERSION_PATTERN` 匹配到的版本行。对于 `pyproject.toml`:
170
+ ```bash
171
+ # 查找当前行:
172
+ grep -n '^\s*version\s*=\s*"[^"]+"' pyproject.toml
173
+ # 用 Edit 工具将该行的 "X.Y.Z" 替换为 "A.B.C"
174
+ ```
175
+
176
+ **4c. 提交:**
177
+
178
+ 检查工作树状态:
179
+ ```bash
180
+ git status --short
181
+ ```
182
+
183
+ - 如果有更改(已暂存或未暂存):暂存所有文件并提交:
184
+ ```bash
185
+ git add -A
186
+ git commit -m "chore: release {version}"
187
+ ```
188
+ - 如果工作树已干净:无需提交,跳过。
189
+
190
+ **4d. 推送 release 分支:**
191
+ ```bash
192
+ git push -u {REMOTE} {RELEASE_BRANCH_PREFIX}{version}
193
+ ```
194
+
195
+ 推送失败则中止。
196
+
197
+ ### 步骤 5 — 发布到 PyPI
198
+
199
+ **自动检测 PUBLISH_CMD**(仅在配置未覆盖时):
200
+
201
+ ```bash
202
+ # 检查 Makefile 是否有 publish 目标
203
+ grep -q '^\s*publish\s*:' Makefile 2>/dev/null && echo "make publish" || echo "python -m build && python -m twine upload dist/*"
204
+ ```
205
+
206
+ - 如果 `make publish` 目标存在 → `PUBLISH_CMD=make publish`
207
+ - 否则 → `PUBLISH_CMD=python -m build && python -m twine upload dist/*`
208
+ - 如果 `python -m build` 或 `python -m twine` 未安装,中止:"缺少构建工具。请安装:`pip install build twine`"
209
+
210
+ 执行:
211
+ ```bash
212
+ {PUBLISH_CMD}
213
+ ```
214
+
215
+ 实时输出。如果退出码非零:
216
+
217
+ ```
218
+ ✗ 发布失败(退出码 N)。已停止 — 不会创建 tag。
219
+ 请检查上方输出,解决问题后重新运行 /publish。
220
+ ```
221
+
222
+ **不继续执行步骤 6。**
223
+
224
+ ### 步骤 6 — 打 tag 并推送
225
+
226
+ ```bash
227
+ git tag {TAG_PREFIX}{version}
228
+ git push {REMOTE} {TAG_PREFIX}{version}
229
+ ```
230
+
231
+ 如果 `git tag` 因 tag 已存在而失败:
232
+ ```
233
+ ✗ Tag {TAG_PREFIX}{version} 已存在。
234
+ 请手动删除:git tag -d {TAG_PREFIX}{version}
235
+ 然后重新运行 /publish 从此步骤重试。
236
+ ```
237
+ 中止。
238
+
239
+ ### 步骤 7 — 创建合并请求
240
+
241
+ 使用工丰 MCP 工具从 release 分支创建到 `TARGET_BRANCH` 的合并请求:
242
+
243
+ 1. 从 git remote 检测项目路径:
244
+ ```bash
245
+ git remote get-url {REMOTE} | sed -E 's|.*[:/](.+/.+)\.git$|\1|'
246
+ ```
247
+
248
+ 2. 使用 `mcp__gongfeng__create_merge_request` 创建 MR:
249
+ - `project_id`:项目完整路径(如 `orcakit/finnie`)
250
+ - `title`:`chore: release {version}`
251
+ - `source_branch`:`{RELEASE_BRANCH_PREFIX}{version}`
252
+ - `target_branch`:`{TARGET_BRANCH}`
253
+ - `description`:步骤 3 生成的 CHANGELOG 条目
254
+
255
+ 3. 成功时展示:
256
+ ```
257
+ ✓ 已发布 {package_name} {version} 到 PyPI
258
+ ✓ 已创建并推送 tag {TAG_PREFIX}{version} 到 {REMOTE}
259
+ ✓ 已创建合并请求:{RELEASE_BRANCH_PREFIX}{version} → {TARGET_BRANCH}
260
+ 链接:{mr_url}
261
+ ```
262
+
263
+ 4. MR 创建失败时展示警告(非致命 — 发布已成功):
264
+ ```
265
+ ⚠ 发布成功,但合并请求创建失败。
266
+ 请手动创建:{RELEASE_BRANCH_PREFIX}{version} → {TARGET_BRANCH}
267
+ ```
268
+
269
+ ### 步骤 8 — 切回原分支
270
+
271
+ 返回创建 release 分支之前所在的分支:
272
+
273
+ ```bash
274
+ git checkout {original_branch}
275
+ ```
276
+
277
+ 确保流程结束后用户不会停留在 release 分支上。
278
+
279
+ ## 错误处理参考
280
+
281
+ | 场景 | 行为 |
282
+ |------|------|
283
+ | `VERSION_FILE` 未找到 | 中止:"找不到 VERSION_FILE:{path}" |
284
+ | 文件中未匹配到版本号 | 中止:"在 {VERSION_FILE} 中找不到匹配 {VERSION_PATTERN} 的版本行" |
285
+ | 没有 git tag(首次发布) | 使用从仓库初始到 HEAD 的所有提交;提示"首次发布 — 使用完整 git 历史" |
286
+ | 上次 tag 以来无提交 | 警告并询问是否继续 |
287
+ | Release 分支已存在 | 中止并给出删除指令 |
288
+ | 步骤 4 推送失败 | 中止:文件已在本地更新但未推送 |
289
+ | 步骤 5 发布失败 | 中止:不创建 tag、不创建 MR |
290
+ | 步骤 6 tag 已存在 | 中止并给出删除指令 |
291
+ | 步骤 7 MR 创建失败 | 仅警告(发布已成功) |
292
+
293
+ ## 红线规则
294
+
295
+ **绝不:**
296
+ - 在发布成功前创建 tag
297
+ - 在发布成功前创建合并请求
298
+ - 跳过步骤 1 的用户确认
299
+ - 跳过步骤 3 的 CHANGELOG 确认
300
+ - 在任何步骤失败后继续执行(步骤 7 MR 失败除外)
301
+ - 流程结束后让用户留在 release 分支
302
+
303
+ **始终:**
304
+ - 任何失败立即中止(步骤 7 除外)
305
+ - 中止前展示完整错误输出
306
+ - 插入新版本条目后保持 `[Unreleased]` 为空
307
+ - 流程结束时切回原分支
@@ -0,0 +1,55 @@
1
+ # OpenAI API Configuration
2
+ # Copy this file to .env and fill in your credentials
3
+
4
+ OPENAI_API_KEY=your_api_key_here
5
+ OPENAI_BASE_URL=https://api.openai.com/v1
6
+ OPENAI_MODEL_NAME=gpt-4o-mini
7
+
8
+ # ---------------------------------------------------------------------------
9
+ # XiaoYi (小艺 / Huawei OpenClaw)
10
+ # ---------------------------------------------------------------------------
11
+ XIAOYI_AK=
12
+ XIAOYI_SK=
13
+ XIAOYI_AGENT_ID=
14
+ # Show thinking / tool messages (true/false)
15
+ XIAOYI_SHOW_THINKING=false
16
+ XIAOYI_SHOW_TOOL_HINTS=true
17
+
18
+ # ---------------------------------------------------------------------------
19
+ # MQTT (EMQX Cloud example)
20
+ # ---------------------------------------------------------------------------
21
+ MQTT_HOST=c14633a9.ala.cn-shenzhen.emqxsl.cn
22
+ MQTT_PORT=8883
23
+ MQTT_USERNAME=
24
+ MQTT_PASSWORD=
25
+ MQTT_SUBSCRIBE_TOPIC=devices/+/in
26
+ MQTT_PUBLISH_TOPIC=devices/{client_id}/out
27
+ MQTT_TLS_ENABLED=true
28
+ # Path to CA PEM file, or set tls_ca_pem inline in code
29
+ MQTT_TLS_CA_FILE=
30
+ MQTT_SHOW_THINKING=false
31
+ MQTT_SHOW_TOOL_HINTS=true
32
+
33
+ # ---------------------------------------------------------------------------
34
+ # Telegram
35
+ # ---------------------------------------------------------------------------
36
+ TELEGRAM_BOT_TOKEN=
37
+ TELEGRAM_HTTP_PROXY=
38
+ TELEGRAM_SHOW_TYPING=true
39
+ TELEGRAM_SHOW_THINKING=false
40
+ TELEGRAM_SHOW_TOOL_HINTS=true
41
+
42
+ # ---------------------------------------------------------------------------
43
+ # Other IM platforms (examples)
44
+ # ---------------------------------------------------------------------------
45
+ FEISHU_APP_ID=
46
+ FEISHU_APP_SECRET=
47
+ QQ_APP_ID=
48
+ QQ_TOKEN=
49
+ QQ_SECRET=
50
+ DINGTALK_APP_KEY=
51
+ DINGTALK_APP_SECRET=
52
+ WECOM_BOT_ID=
53
+ WECOM_SECRET=
54
+ WEIXIN_ACCOUNT_ID=
55
+ WEIXIN_TOKEN=
@@ -0,0 +1,23 @@
1
+ ## Summary
2
+
3
+ <!-- What does this PR change and why? -->
4
+
5
+ ## Type of change
6
+
7
+ - [ ] Bug fix
8
+ - [ ] New feature
9
+ - [ ] Breaking change
10
+ - [ ] Documentation
11
+ - [ ] Refactor / chore
12
+
13
+ ## Test plan
14
+
15
+ <!-- How did you verify this? Include commands run (e.g. `make all`). -->
16
+
17
+ - [ ] `make all` passes locally
18
+ - [ ] Added/updated tests
19
+
20
+ ## Checklist
21
+
22
+ - [ ] Updated `CHANGELOG.md` (if user-facing)
23
+ - [ ] README / docs updated (if needed)
@@ -0,0 +1,38 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ concurrency:
9
+ group: ci-${{ github.ref }}
10
+ cancel-in-progress: true
11
+
12
+ jobs:
13
+ quality:
14
+ name: "Python ${{ matrix.python-version }}"
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ python-version: ["3.11", "3.12"]
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+
23
+ - uses: astral-sh/setup-uv@v4
24
+ with:
25
+ enable-cache: true
26
+ python-version: ${{ matrix.python-version }}
27
+
28
+ - name: Install dependencies
29
+ run: make install
30
+
31
+ - name: Lint
32
+ run: make lint
33
+
34
+ - name: Type check
35
+ run: make typecheck
36
+
37
+ - name: Test
38
+ run: make test
@@ -0,0 +1,30 @@
1
+ name: CodeQL
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+ schedule:
9
+ - cron: "0 3 * * 1"
10
+
11
+ permissions:
12
+ security-events: write
13
+
14
+ jobs:
15
+ analyze:
16
+ name: Analyze
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+
21
+ - name: Initialize CodeQL
22
+ uses: github/codeql-action/init@v3
23
+ with:
24
+ languages: python
25
+
26
+ - name: Autobuild
27
+ uses: github/codeql-action/autobuild@v3
28
+
29
+ - name: Perform CodeQL Analysis
30
+ uses: github/codeql-action/analyze@v3