@comate/zulu 1.4.0-beta.2 → 1.4.0-beta.3
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.
- package/comate-engine/assets/skills/auto-commit/SKILL.md +241 -0
- package/comate-engine/assets/skills/auto-commit/references/data_structures.md +183 -0
- package/comate-engine/assets/skills/auto-commit/references/interaction_instruction.md +220 -0
- package/comate-engine/assets/skills/auto-commit/references/issue_type_mapping.json +19 -0
- package/comate-engine/assets/skills/auto-commit/references/query_reference.md +176 -0
- package/comate-engine/assets/skills/auto-commit/scripts/build_git_commit_payload.py +195 -0
- package/comate-engine/assets/skills/auto-commit/scripts/build_icafe_cards_payload.py +80 -0
- package/comate-engine/assets/skills/auto-commit/scripts/cache_manager.py +69 -0
- package/comate-engine/assets/skills/auto-commit/scripts/create_card_cli.py +67 -0
- package/comate-engine/assets/skills/auto-commit/scripts/git_diff_cli.py +201 -0
- package/comate-engine/assets/skills/auto-commit/scripts/git_utils.py +230 -0
- package/comate-engine/assets/skills/auto-commit/scripts/icafe/__init__.py +66 -0
- package/comate-engine/assets/skills/auto-commit/scripts/icafe/client.py +473 -0
- package/comate-engine/assets/skills/auto-commit/scripts/icafe/farseer.py +52 -0
- package/comate-engine/assets/skills/auto-commit/scripts/icafe/matching.py +784 -0
- package/comate-engine/assets/skills/auto-commit/scripts/logger.py +32 -0
- package/comate-engine/assets/skills/auto-commit/scripts/match_card_cli.py +41 -0
- package/comate-engine/assets/skills/auto-commit/scripts/payload_validators.py +309 -0
- package/comate-engine/assets/skills/auto-commit/scripts/recognize_card_cli.py +63 -0
- package/comate-engine/assets/skills/{automation-browser-comate → automation-browser}/SKILL.md +1 -0
- package/comate-engine/assets/skills/{cnap-comate → cnap}/SKILL.md +1 -0
- package/comate-engine/assets/skills/code-review/SKILL.md +202 -0
- package/comate-engine/assets/skills/code-review/agents/correctness-reviewer.md +62 -0
- package/comate-engine/assets/skills/code-review/agents/custom-reviewer.md +53 -0
- package/comate-engine/assets/skills/code-review/agents/meta-reviewer.md +84 -0
- package/comate-engine/assets/skills/code-review/agents/reliability-reviewer.md +72 -0
- package/comate-engine/assets/skills/code-review/agents/reuse-reviewer.md +101 -0
- package/comate-engine/assets/skills/code-review/agents/style-reviewer.md +65 -0
- package/comate-engine/assets/skills/code-review/evals/SKILL.md +334 -0
- package/comate-engine/assets/skills/code-review/evals/agents/gt-generator.md +76 -0
- package/comate-engine/assets/skills/code-review/evals/agents/miner.md +87 -0
- package/comate-engine/assets/skills/code-review/evals/agents/score-judge.md +168 -0
- package/comate-engine/assets/skills/code-review/evals/references/cli-query-template.md +114 -0
- package/comate-engine/assets/skills/code-review/evals/references/gt-schema.md +77 -0
- package/comate-engine/assets/skills/code-review/references/custom-rules/RULE_TEMPLATE.md +141 -0
- package/comate-engine/assets/skills/code-review/references/dispatch-template.md +142 -0
- package/comate-engine/assets/skills/code-review/references/output-schema.md +197 -0
- package/comate-engine/assets/skills/code-review/references/report-format.md +41 -0
- package/comate-engine/assets/skills/code-review/references/rules/Go/GO_AUTH_RULES.md +29 -0
- package/comate-engine/assets/skills/code-review/references/rules/Go/GO_CORRECTNESS_RULES.md +111 -0
- package/comate-engine/assets/skills/code-review/references/rules/Go/GO_RESOURCE_CONCURRENCY_RULES.md +190 -0
- package/comate-engine/assets/skills/code-review/references/rules/Go/GO_STYLE_RULES.md +354 -0
- package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_AUTH_RULES.md +34 -0
- package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_CORRECTNESS_RULES.md +207 -0
- package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_RESOURCE_CONCURRENCY_RULES.md +220 -0
- package/comate-engine/assets/skills/code-review/references/rules/Java/JAVA_STYLE_RULES.md +306 -0
- package/comate-engine/assets/skills/code-review/references/rules/Js/JS_AUTH_RULES.md +48 -0
- package/comate-engine/assets/skills/code-review/references/rules/Js/JS_CORRECTNESS_RULES.md +364 -0
- package/comate-engine/assets/skills/code-review/references/rules/Js/JS_RESOURCE_CONCURRENCY_RULES.md +180 -0
- package/comate-engine/assets/skills/code-review/references/rules/Js/JS_STYLE_RULES.md +350 -0
- package/comate-engine/assets/skills/code-review/references/rules/Python/PYTHON_AUTH_RULES.md +38 -0
- package/comate-engine/assets/skills/code-review/references/rules/Python/PYTHON_CORRECTNESS_RULES.md +255 -0
- package/comate-engine/assets/skills/code-review/references/rules/Python/PYTHON_RESOURCE_CONCURRENCY_RULES.md +180 -0
- package/comate-engine/assets/skills/code-review/references/rules/Python/PYTHON_STYLE_RULES.md +195 -0
- package/comate-engine/assets/skills/code-review/references/telemetry.md +27 -0
- package/comate-engine/assets/skills/{code-security-comate → code-security}/SKILL.md +1 -0
- package/comate-engine/assets/skills/{comate-docs-comate → comate-docs}/SKILL.md +1 -1
- package/comate-engine/assets/skills/create-automation-tasks-comate/SKILL.md +300 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/backend_dev.md +109 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/env_setup.md +130 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/frontend_dev.md +74 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/git_operations.md +88 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/long_running_task.md +96 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/references/testing_strategy.md +94 -0
- package/comate-engine/assets/skills/create-automation-tasks-comate/scripts/check_config.py +397 -0
- package/comate-engine/assets/skills/{create-rule-comate → create-rule}/SKILL.md +1 -0
- package/comate-engine/assets/skills/{create-skill-comate → create-skill}/SKILL.md +1 -1
- package/comate-engine/assets/skills/{figma2code-comate → figma2code}/SKILL.md +1 -0
- package/comate-engine/assets/skills/{icafe-comate → icafe}/SKILL.md +1 -13
- package/comate-engine/assets/skills/{icode-comate → icode}/SKILL.md +1 -0
- package/comate-engine/node_modules/@comate/plugin-shared-internals/dist/index.js +3 -3
- package/comate-engine/server.js +136 -82
- package/dist/bundle/index.js +20 -9
- package/package.json +1 -1
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/cases.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/deploy-troubleshoot.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/install.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/kubectl.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/login.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/references/oncall.md +0 -0
- /package/comate-engine/assets/skills/{cnap-comate → cnap}/scripts/install_cnap_cli.sh +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/credential_hosting.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/vul_repair-go_sql_injection.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/vul_repair-java_sql_injection.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/vul_repair-php_sql_injection.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/vul_repair-python_sql_injection.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/references/vul_repair_sensitive.md +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/credential_hosting.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/credential_poll.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/http_client.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/parse_scan_result.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/repair_vulnerability.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/report_chat.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/scan_vulnerability.py +0 -0
- /package/comate-engine/assets/skills/{code-security-comate → code-security}/scripts/utils.py +0 -0
- /package/comate-engine/assets/skills/{comate-docs-comate → comate-docs}/references/doc-map-extended.md +0 -0
- /package/comate-engine/assets/skills/{comate-docs-comate → comate-docs}/references/models-and-billing.md +0 -0
- /package/comate-engine/assets/skills/{comate-docs-comate → comate-docs}/references/product-overview.md +0 -0
- /package/comate-engine/assets/skills/{create-image-comate → create-image}/SKILL.md +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/LICENSE.txt +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/agents/analyzer.md +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/agents/comparator.md +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/agents/grader.md +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/assets/eval_review.html +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/eval-viewer/generate_review.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/eval-viewer/viewer.html +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/references/schemas.md +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/__init__.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/__pycache__/__init__.cpython-311.pyc +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/__pycache__/aggregate_benchmark.cpython-311.pyc +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/aggregate_benchmark.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/generate_report.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/package_skill.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/quick_validate.py +0 -0
- /package/comate-engine/assets/skills/{create-skill-comate → create-skill}/scripts/utils.py +0 -0
- /package/comate-engine/assets/skills/{create-subagent-comate → create-subagent}/SKILL.md +0 -0
- /package/comate-engine/assets/skills/{figma2code-comate → figma2code}/references/codeConnect.md +0 -0
- /package/comate-engine/assets/skills/{figma2code-comate → figma2code}/references/designToken.md +0 -0
- /package/comate-engine/assets/skills/{figma2code-comate → figma2code}/references/image2design.md +0 -0
- /package/comate-engine/assets/skills/{find-skills-comate → find-skills}/SKILL.md +0 -0
- /package/comate-engine/assets/skills/{find-skills-comate → find-skills}/scripts/fetch_skills.py +0 -0
- /package/comate-engine/assets/skills/{find-skills-comate → find-skills}/scripts/get_download_url.py +0 -0
- /package/comate-engine/assets/skills/{find-skills-comate → find-skills}/scripts/install_skill.py +0 -0
- /package/comate-engine/assets/skills/{find-skills-comate → find-skills}/scripts/preview_skill.py +0 -0
- /package/comate-engine/assets/skills/{get-ugate-token-comate → get-ugate-token}/SKILL.md +0 -0
- /package/comate-engine/assets/skills/{get-ugate-token-comate → get-ugate-token}/getUgateToken.py +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/ai-workflows.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/commands.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/error-handling.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/git-auto-bindcard-workflow.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/git-bindcard-workflow.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/iql-syntax.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/platform-concepts.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/smart-create-workflow.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/smart-find-workflow.md +0 -0
- /package/comate-engine/assets/skills/{icafe-comate → icafe}/references/smart-update-workflow.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/add_reviewers.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/build_fetch_command.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/check_repo_permission.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/create_branch.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/create_draft_comment.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_ai_cr_result.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_ai_review.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_diff_content.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_diff_file.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_machine_check.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_my_reviews.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_person_commit.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_person_repo.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_repo_branch.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_repo_config.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_repo_members.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_repo_reviews.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_review_comments.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_review_info.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/get_submit_settings.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/icode-api.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/publish_comments.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/set_review_score.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/start_ai_review.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/submit_review.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/api/trigger_ai_cr.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/add-reviewer.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/fix-machine-check.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/merge-cr.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/ssh-setup.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/submit-acr.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/feature/submit-cr.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/git/clone.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/git/icode-git.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/git/push.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/git/push_cr.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/install.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/references/login.md +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/add-reviewer.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/common.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/fix-machine-check.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/merge-cr.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/ssh-setup.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/submit-acr.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/submit-cr.sh +0 -0
- /package/comate-engine/assets/skills/{icode-comate → icode}/scripts/test-preflight.sh +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/SKILL.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/examples.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/add_member.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/change_scope.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/copy_doc.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/create_doc.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/delete_doc.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/edit_content.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/move_doc.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_comment.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_content.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_flowchart.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_permission.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_recent_view.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_repo.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/query_user_info.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/update_member.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/references/upload_attachment.md +0 -0
- /package/comate-engine/assets/skills/{ku-operator-comate → ku-operator}/scripts/ku_operator.py +0 -0
package/comate-engine/assets/skills/create-automation-tasks-comate/references/git_operations.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# Git 操作策略模板
|
|
2
|
+
|
|
3
|
+
当 Automation 任务涉及 Git 操作(拉代码、改代码后提交/push)时,将以下策略融入 query 设计。
|
|
4
|
+
|
|
5
|
+
## 适用场景
|
|
6
|
+
|
|
7
|
+
- 任务需要在执行前拉取最新代码
|
|
8
|
+
- 任务会修改代码并需要提交
|
|
9
|
+
- 任务需要在独立分支上操作以隔离风险
|
|
10
|
+
|
|
11
|
+
## 策略要点
|
|
12
|
+
|
|
13
|
+
### 拉取代码
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
执行前先拉取最新代码:
|
|
17
|
+
git fetch origin {branch}
|
|
18
|
+
git checkout {branch}
|
|
19
|
+
git pull origin {branch}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- 如果 pull 因网络错误失败,最多重试 3 次,间隔指数退避(4s, 8s, 16s)
|
|
23
|
+
- 明确指定分支名,不要用模糊的 `git pull`
|
|
24
|
+
|
|
25
|
+
### 分支管理
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
创建独立工作分支:
|
|
29
|
+
git checkout -b automation/{task-name}-{date} {base-branch}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- 自动化任务建议在独立分支上操作,不直接修改 main/master
|
|
33
|
+
- 分支命名建议:`automation/{task-name}-{YYYYMMDD}`
|
|
34
|
+
- 除非用户明确要求,不要切换到其他已有分支
|
|
35
|
+
|
|
36
|
+
### 提交规范
|
|
37
|
+
|
|
38
|
+
自动化任务默认使用 `git add -A` + 单次 commit:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
git add -A
|
|
42
|
+
git commit -m "{描述性提交信息}"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
- 提交信息要描述做了什么,不要写 "automation update" 这种无信息量的消息
|
|
46
|
+
- 不要 force push 或 amend commit
|
|
47
|
+
- 如果用户在 query 中明确要求按逻辑拆分 commit,则使用 `git add {具体文件}` 分次提交
|
|
48
|
+
|
|
49
|
+
### 提交前清理
|
|
50
|
+
|
|
51
|
+
提交前必须移除所有临时调试代码(extra logs、hard-coded config modifications、临时 feature flag overrides 等)。只有自动化测试代码和测试基础设施可以保留。
|
|
52
|
+
|
|
53
|
+
### Push 策略
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
推送到远程:
|
|
57
|
+
git push origin {branch-name}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
- 如果 push 因网络错误失败,最多重试 4 次,指数退避(4s, 8s, 16s, 32s)
|
|
61
|
+
- 明确指定目标分支,不要 `git push` 裸推
|
|
62
|
+
- 不要 force push,除非用户在 query 中明确要求
|
|
63
|
+
|
|
64
|
+
## query 片段示例
|
|
65
|
+
|
|
66
|
+
**场景:自动修复后推到独立分支**
|
|
67
|
+
```
|
|
68
|
+
执行前拉取最新代码:
|
|
69
|
+
git fetch origin main && git checkout main && git pull origin main
|
|
70
|
+
|
|
71
|
+
创建工作分支:
|
|
72
|
+
git checkout -b automation/dep-fix-$(date +%Y%m%d) main
|
|
73
|
+
|
|
74
|
+
... (执行修复操作) ...
|
|
75
|
+
|
|
76
|
+
提交并推送:
|
|
77
|
+
git add -A
|
|
78
|
+
git commit -m "fix: 自动修复依赖安全漏洞"
|
|
79
|
+
git push origin automation/dep-fix-$(date +%Y%m%d)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**场景:只拉代码生成报告,不修改代码**
|
|
83
|
+
```
|
|
84
|
+
确保代码是最新的:
|
|
85
|
+
git fetch origin main && git checkout main && git pull origin main
|
|
86
|
+
|
|
87
|
+
注意:本次任务只读不写,不需要创建分支、commit 或 push。
|
|
88
|
+
```
|
package/comate-engine/assets/skills/create-automation-tasks-comate/references/long_running_task.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# 长时间/复杂任务策略模板
|
|
2
|
+
|
|
3
|
+
当 Automation 任务预期执行时间较长或步骤较多时,将以下策略融入 query 设计。
|
|
4
|
+
|
|
5
|
+
## 适用场景
|
|
6
|
+
|
|
7
|
+
- 全量代码扫描、安全审计
|
|
8
|
+
- 大型项目的完整测试套件
|
|
9
|
+
- 数据迁移、批量处理
|
|
10
|
+
- 多步骤的端到端流程(拉代码 → 修改 → 测试 → 提交)
|
|
11
|
+
|
|
12
|
+
## 策略要点
|
|
13
|
+
|
|
14
|
+
### 超时管理
|
|
15
|
+
|
|
16
|
+
- 在 frontmatter 中设置充足的 `timeoutSeconds`
|
|
17
|
+
- 简单任务:1800(30分钟)
|
|
18
|
+
- 中等任务:3600(1小时,默认)
|
|
19
|
+
- 复杂任务:7200(2小时,上限)
|
|
20
|
+
|
|
21
|
+
### 分步执行与中间检查点
|
|
22
|
+
|
|
23
|
+
对于多步骤任务,在 query 中明确分步,让 Agent 逐步推进:
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
按以下步骤执行,每步完成后记录结果:
|
|
27
|
+
|
|
28
|
+
步骤 1:...
|
|
29
|
+
步骤 2:...
|
|
30
|
+
步骤 3:...
|
|
31
|
+
|
|
32
|
+
如果任一步骤失败,记录失败原因后继续执行后续步骤(除非后续步骤依赖前序结果)。
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 幂等性
|
|
36
|
+
|
|
37
|
+
Automation 任务可能被重复执行(定时触发、补跑等),query 设计应考虑幂等:
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
注意幂等性:
|
|
41
|
+
- 创建分支前先检查是否已存在同名分支:
|
|
42
|
+
git branch -D automation/dep-fix-20260420 2>/dev/null || true
|
|
43
|
+
git checkout -b automation/dep-fix-20260420 main
|
|
44
|
+
- 生成报告时覆盖旧报告,不要追加产生重复内容
|
|
45
|
+
- 修改代码前先检查是否已经做过相同修改
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 错误处理与自主排障
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
遇到错误时的处理策略:
|
|
52
|
+
- 命令失败:分析错误输出,尝试修复后重试,最多重试 3 次
|
|
53
|
+
- 网络错误:等待后重试,指数退避
|
|
54
|
+
- 权限错误:记录问题,不要尝试 sudo 或修改系统权限
|
|
55
|
+
- 环境问题(非本次修改导致的):至少尝试 3 种不同的修复方案再放弃
|
|
56
|
+
- 不可恢复的错误:记录详细错误信息,跳过该步骤继续执行
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
关键原则:**区分"代码问题"和"环境问题"**。如果测试失败是因为环境配置(缺少依赖、端口占用、服务未启动),应尝试修复环境而非修改代码。至少尝试 3 种不同的修复方案后再判定为阻塞。
|
|
60
|
+
|
|
61
|
+
### 执行摘要规范
|
|
62
|
+
|
|
63
|
+
Automation Agent 的唯一交付物是 `promise_done_summary.md`。对于复杂任务,建议在 query 中明确摘要格式:
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
完成后将执行摘要写入 promise_done_summary.md,包含:
|
|
67
|
+
- 执行了哪些步骤,每步的结果(成功/失败/跳过)
|
|
68
|
+
- 如果有代码修改,列出修改的文件和改动性质
|
|
69
|
+
- 关键的终端输出、测试结果数据(作为验证证据)
|
|
70
|
+
- 遇到的问题和处理方式
|
|
71
|
+
- 需要人工关注的事项(如:测试覆盖不足、某个修复需要人工确认等)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
这份摘要是用户判断任务执行质量的唯一依据,应当清晰、诚实、包含充分证据。如果任务未完全成功,不要写这个文件——让 Session 以 stopped 状态结束,摘要中记录原因即可。
|
|
75
|
+
|
|
76
|
+
## query 片段示例
|
|
77
|
+
|
|
78
|
+
**场景:全量安全扫描 + 自动修复**
|
|
79
|
+
```
|
|
80
|
+
执行全量安全扫描和自动修复(预计耗时较长):
|
|
81
|
+
|
|
82
|
+
步骤 1 - 依赖扫描:
|
|
83
|
+
运行 npm audit,记录发现的漏洞列表
|
|
84
|
+
|
|
85
|
+
步骤 2 - 自动修复:
|
|
86
|
+
对可自动修复的漏洞执行 npm audit fix
|
|
87
|
+
对不能自动修复的,记录在摘要中
|
|
88
|
+
|
|
89
|
+
步骤 3 - 验证:
|
|
90
|
+
运行 npm test 确保修复未引入回归
|
|
91
|
+
|
|
92
|
+
步骤 4 - 提交(仅当步骤 3 通过时):
|
|
93
|
+
git add -A && git commit -m "fix: 自动修复安全漏洞" && git push origin {branch}
|
|
94
|
+
|
|
95
|
+
注意:如果步骤 2 或 3 失败,仍然完成摘要,记录哪些修复了、哪些没修复。
|
|
96
|
+
```
|
package/comate-engine/assets/skills/create-automation-tasks-comate/references/testing_strategy.md
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# 测试与验证策略模板
|
|
2
|
+
|
|
3
|
+
当 Automation 任务涉及代码修改、功能验证、质量检查时,将以下策略融入 query 设计。
|
|
4
|
+
|
|
5
|
+
## 适用场景
|
|
6
|
+
|
|
7
|
+
- 修改代码后需要验证正确性
|
|
8
|
+
- 定期运行测试套件并报告结果
|
|
9
|
+
- Bug 修复需要验证修复有效
|
|
10
|
+
- 功能变更需要端到端验证
|
|
11
|
+
|
|
12
|
+
## 核心原则
|
|
13
|
+
|
|
14
|
+
**不能只靠代码静态推断来判断结果,必须依赖真实运行信息。** 日志、终端输出、测试结果都是重要证据。验证证据应写入 `promise_done_summary.md`。
|
|
15
|
+
|
|
16
|
+
## 策略要点
|
|
17
|
+
|
|
18
|
+
### 定义成功标准(写入 query)
|
|
19
|
+
|
|
20
|
+
根据任务类型,在 query 中明确告诉 Agent 什么算"完成":
|
|
21
|
+
|
|
22
|
+
- **Bug 修复**:先复现 bug(运行触发 bug 的命令/测试),再验证修复后同样步骤不再出现 bug
|
|
23
|
+
- **依赖更新**:更新后所有现有测试仍然通过
|
|
24
|
+
- **新功能**:自动化测试通过 + 关键 API/命令的终端输出证明功能正常工作
|
|
25
|
+
- **性能优化**:修改前后的性能数据对比(通过日志或 benchmark 命令输出)
|
|
26
|
+
|
|
27
|
+
### 识别低信号测试结果
|
|
28
|
+
|
|
29
|
+
以下结果**不足以**证明改动是正确的,不应视为验证通过:
|
|
30
|
+
|
|
31
|
+
- "代码能编译"(除非改动只影响编译系统)
|
|
32
|
+
- "应用能启动"(除非改动只影响启动流程)
|
|
33
|
+
- "相关功能正常,但新代码实际上没有被执行到"
|
|
34
|
+
|
|
35
|
+
在 query 中指导 Agent:如果只能拿到低信号结果,不要声称验证通过,应如实记录在摘要中。
|
|
36
|
+
|
|
37
|
+
### 测试类型选择
|
|
38
|
+
|
|
39
|
+
| 任务类型 | 建议测试方式 |
|
|
40
|
+
|---------|------------|
|
|
41
|
+
| 后端逻辑修改 | 自动化测试(unit/integration) |
|
|
42
|
+
| API 变更 | curl/httpie 验证 + 自动化测试 |
|
|
43
|
+
| 前端代码修改 | 构建验证 + lint + 自动化测试 |
|
|
44
|
+
| 依赖/配置变更 | 全量测试套件 |
|
|
45
|
+
| 纯报告生成 | 无需测试 |
|
|
46
|
+
|
|
47
|
+
### 测试失败处理
|
|
48
|
+
|
|
49
|
+
在 query 中指导 Agent 如何处理测试失败:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
如果测试失败:
|
|
53
|
+
1. 分析失败原因,区分是本次修改导致的还是已有问题
|
|
54
|
+
2. 如果是本次修改导致的,尝试修复并重新测试
|
|
55
|
+
3. 如果是已有问题,记录在摘要中但不阻塞任务完成
|
|
56
|
+
4. 不要把失败的测试标记为通过
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### 项目无测试套件
|
|
60
|
+
|
|
61
|
+
如果项目没有自动化测试:
|
|
62
|
+
- 使用构建成功(`npm run build`、`go build`、`cargo build`)作为基本验证
|
|
63
|
+
- 使用 lint 通过(`npm run lint`、`flake8`)作为代码质量验证
|
|
64
|
+
- 在摘要中标注"项目缺乏自动化测试覆盖,仅通过构建/lint 验证"
|
|
65
|
+
|
|
66
|
+
## query 片段示例
|
|
67
|
+
|
|
68
|
+
**场景:修改代码后验证**
|
|
69
|
+
```
|
|
70
|
+
修改完成后,执行以下验证:
|
|
71
|
+
1. 运行 lint 检查:npm run lint
|
|
72
|
+
2. 运行单元测试:npm test
|
|
73
|
+
3. 如果测试失败,分析原因并尝试修复
|
|
74
|
+
4. 在摘要中列出所有测试结果
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**场景:定期测试报告**
|
|
78
|
+
```
|
|
79
|
+
运行完整测试套件:pytest --tb=short -q
|
|
80
|
+
|
|
81
|
+
无论测试是否全部通过,都要生成报告,包含:
|
|
82
|
+
- 通过/失败/跳过的测试数量
|
|
83
|
+
- 失败测试的具体错误信息
|
|
84
|
+
- 与上次运行的对比(如果可获取)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**场景:端到端验证**
|
|
88
|
+
```
|
|
89
|
+
验证策略:
|
|
90
|
+
1. 运行 npm run build 确保构建成功
|
|
91
|
+
2. 运行 npm test 确保自动化测试通过
|
|
92
|
+
3. 如果涉及 API 变更,用 curl 验证关键端点响应正确
|
|
93
|
+
4. 如果构建或测试失败,调查根因并尝试修复,最多重试 3 次
|
|
94
|
+
```
|
|
@@ -0,0 +1,397 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Comate Automation AUTOMATION.md validator.
|
|
4
|
+
Parses YAML frontmatter from AUTOMATION.md, validates all fields (camelCase).
|
|
5
|
+
Usage: python3 check_config.py <path/to/AUTOMATION.md>
|
|
6
|
+
Exit 0 = valid, Exit 1 = invalid.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import sys
|
|
10
|
+
import re
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
try:
|
|
15
|
+
import yaml
|
|
16
|
+
except ImportError:
|
|
17
|
+
print("ERROR: pyyaml not installed. Run: pip install pyyaml")
|
|
18
|
+
sys.exit(1)
|
|
19
|
+
|
|
20
|
+
# ── IANA timezone set ─────────────────────────────────────────────────────────
|
|
21
|
+
try:
|
|
22
|
+
from zoneinfo import available_timezones
|
|
23
|
+
VALID_TIMEZONES = available_timezones()
|
|
24
|
+
except ImportError:
|
|
25
|
+
VALID_TIMEZONES = {
|
|
26
|
+
"UTC", "Asia/Shanghai", "Asia/Tokyo", "Asia/Seoul", "Asia/Singapore",
|
|
27
|
+
"Asia/Hong_Kong", "Asia/Kolkata", "Asia/Dubai", "Europe/London",
|
|
28
|
+
"Europe/Paris", "Europe/Berlin", "Europe/Moscow", "America/New_York",
|
|
29
|
+
"America/Chicago", "America/Denver", "America/Los_Angeles",
|
|
30
|
+
"America/Sao_Paulo", "Africa/Cairo", "Pacific/Auckland",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
# ── helpers ───────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
_errors = []
|
|
36
|
+
_warnings = []
|
|
37
|
+
|
|
38
|
+
def err(msg: str) -> None:
|
|
39
|
+
"""Append an error message to the global error list."""
|
|
40
|
+
_errors.append(f" [ERROR] {msg}")
|
|
41
|
+
|
|
42
|
+
def warn(msg: str) -> None:
|
|
43
|
+
"""Append a warning message to the global warning list."""
|
|
44
|
+
_warnings.append(f" [WARN] {msg}")
|
|
45
|
+
|
|
46
|
+
def is_valid_cron(expr: str) -> bool:
|
|
47
|
+
"""Return True if expr is a valid 5-field cron expression."""
|
|
48
|
+
fields = expr.strip().split()
|
|
49
|
+
if len(fields) != 5:
|
|
50
|
+
return False
|
|
51
|
+
field_pat = r"^(\*|(\*/\d+)|(\d+(-\d+)?(,\d+(-\d+)?)*))$"
|
|
52
|
+
return all(re.match(field_pat, f) for f in fields)
|
|
53
|
+
|
|
54
|
+
def is_valid_iso8601(s: str) -> bool:
|
|
55
|
+
"""Return True if s matches ISO 8601 datetime with timezone offset."""
|
|
56
|
+
return bool(re.match(
|
|
57
|
+
r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(Z|[+-]\d{2}:\d{2})$", str(s)
|
|
58
|
+
))
|
|
59
|
+
|
|
60
|
+
def is_valid_hex8(s: str) -> bool:
|
|
61
|
+
"""Return True if s is an 8-character lowercase hexadecimal string."""
|
|
62
|
+
return bool(re.match(r"^[0-9a-f]{8}$", str(s)))
|
|
63
|
+
|
|
64
|
+
def is_string(v) -> bool:
|
|
65
|
+
"""Return True if v is a non-empty string after stripping whitespace."""
|
|
66
|
+
return isinstance(v, str) and len(v.strip()) > 0
|
|
67
|
+
|
|
68
|
+
# ── snake_case detection ──────────────────────────────────────────────────────
|
|
69
|
+
|
|
70
|
+
KNOWN_SNAKE_TO_CAMEL = {
|
|
71
|
+
"trigger_at": "triggerAt",
|
|
72
|
+
"missed_policy": "missedPolicy",
|
|
73
|
+
"timeout_seconds": "timeoutSeconds",
|
|
74
|
+
"endpoint_id": "endpointId",
|
|
75
|
+
"body_contains": "bodyContains",
|
|
76
|
+
"required_headers": "requiredHeaders",
|
|
77
|
+
"channel_id": "channelId",
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
def check_snake_case_keys(d, path=""):
|
|
81
|
+
"""Recursively warn about snake_case keys that should be camelCase."""
|
|
82
|
+
if not isinstance(d, dict):
|
|
83
|
+
return
|
|
84
|
+
for k, v in d.items():
|
|
85
|
+
full = f"{path}.{k}" if path else k
|
|
86
|
+
if k in KNOWN_SNAKE_TO_CAMEL:
|
|
87
|
+
warn(f"'{full}' uses snake_case. Use camelCase: '{KNOWN_SNAKE_TO_CAMEL[k]}'")
|
|
88
|
+
check_snake_case_keys(v, full)
|
|
89
|
+
if isinstance(v, list):
|
|
90
|
+
for i, item in enumerate(v):
|
|
91
|
+
check_snake_case_keys(item, f"{full}[{i}]")
|
|
92
|
+
|
|
93
|
+
# ── frontmatter parser ────────────────────────────────────────────────────────
|
|
94
|
+
|
|
95
|
+
def parse_frontmatter(text: str):
|
|
96
|
+
"""Parse YAML frontmatter from AUTOMATION.md. Returns (cfg_dict, body_str) or (None, error)."""
|
|
97
|
+
text = text.lstrip("\ufeff") # strip BOM
|
|
98
|
+
if not text.startswith("---"):
|
|
99
|
+
return None, "File must start with '---' (YAML frontmatter)"
|
|
100
|
+
rest = text[3:]
|
|
101
|
+
end = rest.find("\n---")
|
|
102
|
+
if end == -1:
|
|
103
|
+
return None, "No closing '---' found for YAML frontmatter"
|
|
104
|
+
yaml_str = rest[:end]
|
|
105
|
+
body = rest[end + 4:].strip() # after closing ---
|
|
106
|
+
try:
|
|
107
|
+
cfg = yaml.safe_load(yaml_str)
|
|
108
|
+
except yaml.YAMLError as e:
|
|
109
|
+
return None, f"YAML parse error in frontmatter: {e}"
|
|
110
|
+
if not isinstance(cfg, dict):
|
|
111
|
+
return None, "Frontmatter must be a YAML mapping (dict)"
|
|
112
|
+
return (cfg, body), None
|
|
113
|
+
|
|
114
|
+
# ── section validators (camelCase) ────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
def validate_top_level(cfg: dict) -> None:
|
|
117
|
+
"""Validate top-level required fields: name, enabled, description, execution."""
|
|
118
|
+
for field in ("name", "enabled"):
|
|
119
|
+
if field not in cfg:
|
|
120
|
+
err(f"Missing required field: '{field}'")
|
|
121
|
+
|
|
122
|
+
if "name" in cfg:
|
|
123
|
+
if not re.match(r"^[a-z0-9]+(-[a-z0-9]+)*$", str(cfg["name"])):
|
|
124
|
+
err(f"'name' must be kebab-case (e.g. 'daily-dep-check'), got: '{cfg['name']}'")
|
|
125
|
+
|
|
126
|
+
if "enabled" in cfg and not isinstance(cfg["enabled"], bool):
|
|
127
|
+
err(f"'enabled' must be boolean (true/false), got: {cfg['enabled']!r}")
|
|
128
|
+
|
|
129
|
+
if "description" in cfg and not is_string(cfg["description"]):
|
|
130
|
+
err(f"'description' must be a non-empty string, got: {cfg['description']!r}")
|
|
131
|
+
|
|
132
|
+
if "execution" not in cfg:
|
|
133
|
+
err("Missing required block: 'execution'")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def validate_execution(execution: dict) -> None:
|
|
137
|
+
"""Validate the 'execution' block: workspace, model, timeoutSeconds."""
|
|
138
|
+
if not isinstance(execution, dict):
|
|
139
|
+
err("'execution' must be a mapping")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
if "workspace" not in execution:
|
|
143
|
+
err("Missing required field: 'execution.workspace'")
|
|
144
|
+
else:
|
|
145
|
+
ws = str(execution["workspace"])
|
|
146
|
+
if not ws.startswith("/") and not ws.startswith("~"):
|
|
147
|
+
err(f"'execution.workspace' must be an absolute path, got: '{ws}'")
|
|
148
|
+
|
|
149
|
+
if "model" in execution:
|
|
150
|
+
if not is_string(execution["model"]):
|
|
151
|
+
err(f"'execution.model' must be a non-empty string, got: {execution['model']!r}")
|
|
152
|
+
|
|
153
|
+
if "timeoutSeconds" in execution:
|
|
154
|
+
t = execution["timeoutSeconds"]
|
|
155
|
+
if not isinstance(t, int) or t <= 0:
|
|
156
|
+
err(f"'execution.timeoutSeconds' must be a positive integer, got: {t!r}")
|
|
157
|
+
elif t > 7200:
|
|
158
|
+
warn(f"'execution.timeoutSeconds' is {t}s, exceeds recommended max of 7200s")
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def validate_schedule_block(schedule: dict, prefix: str = "schedule") -> None:
|
|
162
|
+
"""Validate a schedule trigger block: cron, triggerAt, timezone, missedPolicy."""
|
|
163
|
+
if not isinstance(schedule, dict):
|
|
164
|
+
err(f"'{prefix}' must be a mapping")
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
for field in ("cron",):
|
|
168
|
+
if field not in schedule:
|
|
169
|
+
err(f"Missing required field: '{prefix}.{field}'")
|
|
170
|
+
|
|
171
|
+
# triggerAt is optional (Phase 2), validate format if present
|
|
172
|
+
if "triggerAt" in schedule:
|
|
173
|
+
if not is_valid_iso8601(str(schedule["triggerAt"])):
|
|
174
|
+
err(
|
|
175
|
+
f"'{prefix}.triggerAt' is not valid ISO 8601"
|
|
176
|
+
f" (e.g. 2026-04-14T09:00:00+08:00), got: '{schedule['triggerAt']}'"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if "cron" in schedule:
|
|
180
|
+
cron = str(schedule["cron"])
|
|
181
|
+
if not is_valid_cron(cron):
|
|
182
|
+
err(f"'{prefix}.cron' is not a valid 5-field cron expression (min hour day month weekday), got: '{cron}'")
|
|
183
|
+
|
|
184
|
+
if "timezone" in schedule:
|
|
185
|
+
tz = str(schedule["timezone"])
|
|
186
|
+
if tz not in VALID_TIMEZONES:
|
|
187
|
+
err(f"'{prefix}.timezone' is not a recognized IANA timezone, got: '{tz}'")
|
|
188
|
+
|
|
189
|
+
if "missedPolicy" in schedule:
|
|
190
|
+
policy = schedule["missedPolicy"]
|
|
191
|
+
if policy not in ("catchup_once", "skip_missed"):
|
|
192
|
+
err(f"'{prefix}.missedPolicy' must be 'catchup_once' or 'skip_missed', got: '{policy}'")
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def validate_webhook_block(webhook: dict, prefix: str) -> None:
|
|
196
|
+
"""Validate a webhook trigger block: endpointId, method, filter."""
|
|
197
|
+
if not isinstance(webhook, dict):
|
|
198
|
+
err(f"'{prefix}.webhook' must be a mapping")
|
|
199
|
+
return
|
|
200
|
+
|
|
201
|
+
if "endpointId" not in webhook:
|
|
202
|
+
err(f"Missing required field: '{prefix}.webhook.endpointId' "
|
|
203
|
+
"(generate: node -e \"console.log(require('crypto').randomBytes(4).toString('hex'))\")")
|
|
204
|
+
elif not is_valid_hex8(str(webhook["endpointId"])):
|
|
205
|
+
err(f"'{prefix}.webhook.endpointId' must be an 8-char lowercase hex string, got: '{webhook['endpointId']}'")
|
|
206
|
+
|
|
207
|
+
if "method" in webhook:
|
|
208
|
+
if webhook["method"] not in ("GET", "POST"):
|
|
209
|
+
err(f"'{prefix}.webhook.method' must be 'GET' or 'POST', got: '{webhook['method']}'")
|
|
210
|
+
|
|
211
|
+
if "filter" in webhook:
|
|
212
|
+
f = webhook["filter"]
|
|
213
|
+
if not isinstance(f, dict):
|
|
214
|
+
err(f"'{prefix}.webhook.filter' must be a mapping")
|
|
215
|
+
else:
|
|
216
|
+
if "bodyContains" in f and not is_string(f["bodyContains"]):
|
|
217
|
+
err(f"'{prefix}.webhook.filter.bodyContains' must be a non-empty string")
|
|
218
|
+
if "requiredHeaders" in f:
|
|
219
|
+
rh = f["requiredHeaders"]
|
|
220
|
+
if not isinstance(rh, list) or not all(isinstance(h, str) for h in rh):
|
|
221
|
+
err(f"'{prefix}.webhook.filter.requiredHeaders' must be a list of strings")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def validate_github_block(github: dict, prefix: str) -> None:
|
|
225
|
+
"""Validate a github trigger block: event, repository, branch."""
|
|
226
|
+
if not isinstance(github, dict):
|
|
227
|
+
err(f"'{prefix}.github' must be a mapping")
|
|
228
|
+
return
|
|
229
|
+
|
|
230
|
+
valid_events = (
|
|
231
|
+
"push", "pull_request_opened", "pull_request_closed",
|
|
232
|
+
"pull_request_merged", "ci_completed", "issue_created", "issue_closed",
|
|
233
|
+
)
|
|
234
|
+
if "event" not in github:
|
|
235
|
+
err(f"Missing required field: '{prefix}.github.event'")
|
|
236
|
+
elif github["event"] not in valid_events:
|
|
237
|
+
warn(f"'{prefix}.github.event' value '{github['event']}' is unrecognized. Known: {valid_events}")
|
|
238
|
+
|
|
239
|
+
if "repository" in github and not is_string(github["repository"]):
|
|
240
|
+
err(f"'{prefix}.github.repository' must be a non-empty string (e.g. 'org/repo')")
|
|
241
|
+
|
|
242
|
+
if "branch" in github and not is_string(github["branch"]):
|
|
243
|
+
err(f"'{prefix}.github.branch' must be a non-empty string")
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def validate_slack_block(slack: dict, prefix: str) -> None:
|
|
247
|
+
"""Validate a slack trigger block: event, channelId."""
|
|
248
|
+
if not isinstance(slack, dict):
|
|
249
|
+
err(f"'{prefix}.slack' must be a mapping")
|
|
250
|
+
return
|
|
251
|
+
|
|
252
|
+
valid_events = ("app_mention", "message", "reaction_added", "file_shared", "channel_created")
|
|
253
|
+
if "event" not in slack:
|
|
254
|
+
err(f"Missing required field: '{prefix}.slack.event'")
|
|
255
|
+
elif slack["event"] not in valid_events:
|
|
256
|
+
warn(f"'{prefix}.slack.event' value '{slack['event']}' is unrecognized. Known: {valid_events}")
|
|
257
|
+
|
|
258
|
+
if "channelId" in slack and not is_string(slack["channelId"]):
|
|
259
|
+
err(f"'{prefix}.slack.channelId' must be a non-empty string")
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def validate_single_trigger(trigger: dict, idx: int) -> Optional[str]:
|
|
263
|
+
"""Validate a single trigger entry by type; return the trigger type string or None."""
|
|
264
|
+
prefix = f"triggers[{idx}]"
|
|
265
|
+
valid_types = ("schedule", "webhook", "github", "slack")
|
|
266
|
+
|
|
267
|
+
if not isinstance(trigger, dict):
|
|
268
|
+
err(f"'{prefix}' must be a mapping")
|
|
269
|
+
return None
|
|
270
|
+
|
|
271
|
+
trigger_type = trigger.get("type")
|
|
272
|
+
if not trigger_type:
|
|
273
|
+
err(f"Missing required field: '{prefix}.type'")
|
|
274
|
+
return None
|
|
275
|
+
|
|
276
|
+
if trigger_type not in valid_types:
|
|
277
|
+
err(f"'{prefix}.type' must be one of {valid_types}, got: '{trigger_type}'")
|
|
278
|
+
return trigger_type
|
|
279
|
+
|
|
280
|
+
if trigger_type == "schedule":
|
|
281
|
+
if "schedule" not in trigger:
|
|
282
|
+
err(f"Missing required block: '{prefix}.schedule' (required when type=schedule)")
|
|
283
|
+
else:
|
|
284
|
+
validate_schedule_block(trigger["schedule"], prefix=f"{prefix}.schedule")
|
|
285
|
+
|
|
286
|
+
elif trigger_type == "webhook":
|
|
287
|
+
if "webhook" not in trigger:
|
|
288
|
+
err(f"Missing required block: '{prefix}.webhook'")
|
|
289
|
+
else:
|
|
290
|
+
validate_webhook_block(trigger["webhook"], prefix=prefix)
|
|
291
|
+
|
|
292
|
+
elif trigger_type == "github":
|
|
293
|
+
if "github" not in trigger:
|
|
294
|
+
err(f"Missing required block: '{prefix}.github'")
|
|
295
|
+
else:
|
|
296
|
+
validate_github_block(trigger["github"], prefix=prefix)
|
|
297
|
+
|
|
298
|
+
elif trigger_type == "slack":
|
|
299
|
+
if "slack" not in trigger:
|
|
300
|
+
err(f"Missing required block: '{prefix}.slack'")
|
|
301
|
+
else:
|
|
302
|
+
validate_slack_block(trigger["slack"], prefix=prefix)
|
|
303
|
+
|
|
304
|
+
return trigger_type
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def validate_triggers(cfg: dict) -> None:
|
|
308
|
+
"""Validate the 'triggers' list: non-empty, each entry valid, at most one schedule."""
|
|
309
|
+
if "triggers" not in cfg:
|
|
310
|
+
err("Missing required field: 'triggers' (list of trigger configurations)")
|
|
311
|
+
return
|
|
312
|
+
|
|
313
|
+
triggers = cfg["triggers"]
|
|
314
|
+
if not isinstance(triggers, list) or len(triggers) == 0:
|
|
315
|
+
err("'triggers' must be a non-empty list")
|
|
316
|
+
return
|
|
317
|
+
|
|
318
|
+
schedule_count = 0
|
|
319
|
+
for i, t in enumerate(triggers):
|
|
320
|
+
ttype = validate_single_trigger(t, i)
|
|
321
|
+
if ttype == "schedule":
|
|
322
|
+
schedule_count += 1
|
|
323
|
+
|
|
324
|
+
if schedule_count > 1:
|
|
325
|
+
err(f"At most one trigger of type 'schedule' is allowed, found {schedule_count}")
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def validate_body(body: str) -> None:
|
|
329
|
+
"""Warn if the AUTOMATION.md body (agent task instructions) is empty."""
|
|
330
|
+
if not body or not body.strip():
|
|
331
|
+
warn("AUTOMATION.md body (query) is empty. The executing Agent will have no task instructions.")
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
# ── main ──────────────────────────────────────────────────────────────────────
|
|
335
|
+
|
|
336
|
+
def validate(path: str) -> bool:
|
|
337
|
+
"""Validate an AUTOMATION.md (or .yaml) config file; return True if valid."""
|
|
338
|
+
global _errors, _warnings
|
|
339
|
+
_errors, _warnings = [], []
|
|
340
|
+
|
|
341
|
+
config_path = Path(path)
|
|
342
|
+
if not config_path.exists():
|
|
343
|
+
print(f"File not found: {path}")
|
|
344
|
+
return False
|
|
345
|
+
|
|
346
|
+
text = config_path.read_text(encoding="utf-8")
|
|
347
|
+
|
|
348
|
+
# Detect file type: .md → parse frontmatter, .yaml/.yml → parse as raw YAML
|
|
349
|
+
if config_path.suffix in (".md",):
|
|
350
|
+
result, error = parse_frontmatter(text)
|
|
351
|
+
if error:
|
|
352
|
+
print(f" [ERROR] {error}")
|
|
353
|
+
print(f"\nValidating: {path}")
|
|
354
|
+
print(f" ✗ Found 1 error(s). Fix the issues above and re-run.")
|
|
355
|
+
return False
|
|
356
|
+
cfg, body = result
|
|
357
|
+
validate_body(body)
|
|
358
|
+
else:
|
|
359
|
+
# Legacy .yaml support
|
|
360
|
+
try:
|
|
361
|
+
cfg = yaml.safe_load(text)
|
|
362
|
+
except yaml.YAMLError as e:
|
|
363
|
+
print(f"YAML parse error: {e}")
|
|
364
|
+
return False
|
|
365
|
+
if not isinstance(cfg, dict):
|
|
366
|
+
print("ERROR: file must contain a YAML mapping (dict)")
|
|
367
|
+
return False
|
|
368
|
+
body = None
|
|
369
|
+
|
|
370
|
+
# Warn about snake_case keys
|
|
371
|
+
check_snake_case_keys(cfg)
|
|
372
|
+
|
|
373
|
+
validate_top_level(cfg)
|
|
374
|
+
validate_triggers(cfg)
|
|
375
|
+
if "execution" in cfg:
|
|
376
|
+
validate_execution(cfg["execution"])
|
|
377
|
+
|
|
378
|
+
print(f"\nValidating: {path}")
|
|
379
|
+
for line in _warnings:
|
|
380
|
+
print(line)
|
|
381
|
+
for line in _errors:
|
|
382
|
+
print(line)
|
|
383
|
+
|
|
384
|
+
if not _errors:
|
|
385
|
+
print(" ✓ AUTOMATION.md is valid")
|
|
386
|
+
return True
|
|
387
|
+
else:
|
|
388
|
+
print(f" ✗ Found {len(_errors)} error(s). Fix the issues above and re-run.")
|
|
389
|
+
return False
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
if __name__ == "__main__":
|
|
393
|
+
if len(sys.argv) != 2:
|
|
394
|
+
print("Usage: python3 check_config.py <path/to/AUTOMATION.md>")
|
|
395
|
+
sys.exit(1)
|
|
396
|
+
ok = validate(sys.argv[1])
|
|
397
|
+
sys.exit(0 if ok else 1)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: create-rule
|
|
3
3
|
description: Create rules for persistent AI guidance. Use when the user wants to create a rule, add coding standards, set up project conventions, configure file-specific patterns.
|
|
4
|
+
disable-model-invocation: true
|
|
4
5
|
metadata:
|
|
5
6
|
disableWhen:
|
|
6
7
|
- isVisualStudio
|