@fitlab-ai/agent-infra 0.5.3 → 0.5.5
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/README.md +1 -1
- package/README.zh-CN.md +1 -1
- package/lib/defaults.json +1 -0
- package/package.json +1 -1
- package/templates/.agents/rules/issue-pr-commands.github.en.md +35 -10
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +35 -10
- package/templates/.agents/rules/issue-sync.github.en.md +118 -28
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +112 -22
- package/templates/.agents/rules/milestone-inference.github.en.md +13 -6
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +13 -6
- package/templates/.agents/rules/pr-sync.github.en.md +3 -1
- package/templates/.agents/rules/pr-sync.github.zh-CN.md +3 -1
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +1118 -0
- package/templates/.agents/scripts/validate-artifact.js +51 -802
- package/templates/.agents/skills/analyze-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/analyze-task/config/verify.json +1 -1
- package/templates/.agents/skills/block-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/block-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/block-task/config/verify.json +1 -1
- package/templates/.agents/skills/cancel-task/SKILL.en.md +6 -6
- package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +6 -6
- package/templates/.agents/skills/cancel-task/config/verify.json +1 -1
- package/templates/.agents/skills/commit/SKILL.en.md +14 -2
- package/templates/.agents/skills/commit/SKILL.zh-CN.md +14 -2
- package/templates/.agents/skills/commit/config/verify.json +2 -1
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.en.md +23 -0
- package/templates/.agents/skills/commit/reference/issue-metadata-sync.zh-CN.md +23 -0
- package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/complete-task/config/verify.json +1 -1
- package/templates/.agents/skills/create-issue/SKILL.en.md +3 -1
- package/templates/.agents/skills/create-issue/SKILL.zh-CN.md +3 -1
- package/templates/.agents/skills/create-issue/config/verify.json +1 -1
- package/templates/.agents/skills/create-issue/reference/label-and-type.en.md +6 -1
- package/templates/.agents/skills/create-issue/reference/label-and-type.zh-CN.md +6 -1
- package/templates/.agents/skills/create-pr/SKILL.en.md +4 -4
- package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/create-pr/config/verify.json +3 -1
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +9 -6
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +9 -6
- package/templates/.agents/skills/create-release-note/SKILL.en.md +27 -2
- package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +27 -2
- package/templates/.agents/skills/create-task/config/verify.json +1 -1
- package/templates/.agents/skills/implement-task/SKILL.en.md +4 -4
- package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +4 -4
- package/templates/.agents/skills/implement-task/config/verify.json +1 -2
- package/templates/.agents/skills/import-codescan/config/verify.json +1 -1
- package/templates/.agents/skills/import-dependabot/config/verify.json +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/config/verify.json +1 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/plan-task/config/verify.json +1 -1
- package/templates/.agents/skills/refine-task/SKILL.en.md +2 -4
- package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +2 -4
- package/templates/.agents/skills/refine-task/config/verify.json +1 -2
- package/templates/.agents/skills/refine-title/SKILL.en.md +5 -1
- package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +5 -1
- package/templates/.agents/skills/restore-task/config/verify.json +1 -1
- package/templates/.agents/skills/review-task/SKILL.en.md +2 -2
- package/templates/.agents/skills/review-task/SKILL.zh-CN.md +2 -2
- package/templates/.agents/skills/review-task/config/verify.json +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +2 -1
- package/templates/.github/scripts/sync-labels-to-set.sh +110 -0
- package/templates/.github/workflows/metadata-sync.yml +118 -0
- package/templates/.github/workflows/pr-label.yml +66 -0
- package/templates/.github/workflows/status-label.yml +20 -30
package/README.md
CHANGED
|
@@ -410,7 +410,7 @@ The generated `.agents/.airc.json` file is the central contract between the boot
|
|
|
410
410
|
"project": "my-project",
|
|
411
411
|
"org": "my-org",
|
|
412
412
|
"language": "en",
|
|
413
|
-
"templateVersion": "v0.5.
|
|
413
|
+
"templateVersion": "v0.5.5",
|
|
414
414
|
"files": {
|
|
415
415
|
"managed": [
|
|
416
416
|
".agents/workspace/README.md",
|
package/README.zh-CN.md
CHANGED
package/lib/defaults.json
CHANGED
package/package.json
CHANGED
|
@@ -13,50 +13,65 @@ gh repo view --json nameWithOwner
|
|
|
13
13
|
|
|
14
14
|
If either command fails, stop or degrade according to the calling skill.
|
|
15
15
|
|
|
16
|
+
## Upstream Repository and Permission Detection
|
|
17
|
+
|
|
18
|
+
Before any later `gh issue` or `gh api "repos/..."` call, follow `.agents/rules/issue-sync.md` to resolve `upstream_repo`, `has_triage`, and `has_push`.
|
|
19
|
+
|
|
20
|
+
- every later `gh issue` command must use `-R "$upstream_repo"`
|
|
21
|
+
- every later repository-scoped `gh api` command must use `"repos/$upstream_repo/..."`
|
|
22
|
+
- keep `gh pr *` commands on the current repository without adding `-R`
|
|
23
|
+
- keep organization-scoped commands such as `gh api "orgs/{owner}/..."` unchanged
|
|
24
|
+
|
|
16
25
|
## Read and Create Issues
|
|
17
26
|
|
|
18
27
|
Read an Issue:
|
|
19
28
|
|
|
20
29
|
```bash
|
|
21
|
-
gh issue view {issue-number} --json number,title,body,labels,state,milestone,url
|
|
30
|
+
gh issue view {issue-number} -R "$upstream_repo" --json number,title,body,labels,state,milestone,url
|
|
22
31
|
```
|
|
23
32
|
|
|
24
33
|
Create an Issue:
|
|
25
34
|
|
|
26
35
|
```bash
|
|
27
|
-
gh issue create --title "{title}" --body "{body}" --assignee @me {label-args} {milestone-arg}
|
|
36
|
+
gh issue create -R "$upstream_repo" --title "{title}" --body "{body}" --assignee @me {label-args} {milestone-arg}
|
|
28
37
|
```
|
|
29
38
|
|
|
30
39
|
- expand `{label-args}` into repeated `--label` flags from the validated label list
|
|
40
|
+
- pass `{label-args}` only when `has_triage=true`; otherwise omit it and continue
|
|
31
41
|
- omit all `--label` flags when nothing valid remains
|
|
42
|
+
- pass `{milestone-arg}` only when `has_triage=true`; otherwise omit it and continue
|
|
32
43
|
- omit `{milestone-arg}` entirely when no milestone should be set
|
|
33
44
|
|
|
34
45
|
Set the Issue Type:
|
|
35
46
|
|
|
36
47
|
```bash
|
|
37
48
|
gh api "orgs/{owner}/issue-types" --jq '.[].name'
|
|
38
|
-
gh api "repos/
|
|
49
|
+
gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
|
|
39
50
|
```
|
|
40
51
|
|
|
52
|
+
- set the Issue Type only when `has_push=true`; otherwise skip and continue
|
|
53
|
+
|
|
41
54
|
## Update Issues
|
|
42
55
|
|
|
43
56
|
Use this shape when updating titles, labels, assignees, or milestones:
|
|
44
57
|
|
|
45
58
|
```bash
|
|
46
|
-
gh issue edit {issue-number} {edit-args}
|
|
59
|
+
gh issue edit {issue-number} -R "$upstream_repo" {edit-args}
|
|
47
60
|
```
|
|
48
61
|
|
|
49
62
|
Common arguments:
|
|
50
63
|
- `--title "{title}"`
|
|
51
|
-
- `--add-label "{label}"`
|
|
52
|
-
- `--remove-label "{label}"`
|
|
64
|
+
- `--add-label "{label}"` (only when `has_triage=true`)
|
|
65
|
+
- `--remove-label "{label}"` (only when `has_triage=true`)
|
|
53
66
|
- `--add-assignee @me`
|
|
54
|
-
- `--milestone "{milestone}"`
|
|
67
|
+
- `--milestone "{milestone}"` (only when `has_triage=true`)
|
|
68
|
+
|
|
69
|
+
Do not pre-check assignee permissions. If the assignee command fails, silently skip it per the caller's contract.
|
|
55
70
|
|
|
56
71
|
Close an Issue:
|
|
57
72
|
|
|
58
73
|
```bash
|
|
59
|
-
gh issue close {issue-number} --reason "{reason}"
|
|
74
|
+
gh issue close {issue-number} -R "$upstream_repo" --reason "{reason}"
|
|
60
75
|
```
|
|
61
76
|
|
|
62
77
|
## Read Issue Comments
|
|
@@ -64,7 +79,7 @@ gh issue close {issue-number} --reason "{reason}"
|
|
|
64
79
|
Read Issue comments or search for existing hidden markers:
|
|
65
80
|
|
|
66
81
|
```bash
|
|
67
|
-
gh api "repos/
|
|
82
|
+
gh api "repos/$upstream_repo/issues/{issue-number}/comments" --paginate
|
|
68
83
|
```
|
|
69
84
|
|
|
70
85
|
## Read and Create PRs
|
|
@@ -84,12 +99,21 @@ gh pr list --state {state} --base {base-branch} --json number,title,url,headRefN
|
|
|
84
99
|
Create a PR:
|
|
85
100
|
|
|
86
101
|
```bash
|
|
87
|
-
gh pr create --base "{target-branch}" --title "{title}" --assignee @me
|
|
102
|
+
gh pr create --base "{target-branch}" --title "{title}" --assignee @me \
|
|
103
|
+
{label-args} {milestone-arg} \
|
|
104
|
+
--body "$(cat <<'EOF'
|
|
88
105
|
{pr-body}
|
|
89
106
|
EOF
|
|
90
107
|
)"
|
|
91
108
|
```
|
|
92
109
|
|
|
110
|
+
- expand `{label-args}` into repeated `--label "{label}"` flags from the validated label list
|
|
111
|
+
- pass `{label-args}` only when `has_triage=true`; otherwise omit it and continue
|
|
112
|
+
- omit all `--label` flags when nothing valid remains
|
|
113
|
+
- expand `{milestone-arg}` into `--milestone "{milestone}"`
|
|
114
|
+
- pass `{milestone-arg}` only when `has_triage=true`; otherwise omit it and continue
|
|
115
|
+
- omit `{milestone-arg}` entirely when no milestone should be set
|
|
116
|
+
|
|
93
117
|
## Update PRs
|
|
94
118
|
|
|
95
119
|
Update PR titles, labels, or milestones with:
|
|
@@ -108,4 +132,5 @@ Common arguments:
|
|
|
108
132
|
|
|
109
133
|
- read failures: stop or skip based on the calling skill
|
|
110
134
|
- update failures: warn and continue when the caller marks the action as best-effort
|
|
135
|
+
- insufficient permission: skip direct writes according to the `has_triage` / `has_push` branch and continue
|
|
111
136
|
- `@me` is resolved by `gh` CLI to the authenticated user
|
|
@@ -13,50 +13,65 @@ gh repo view --json nameWithOwner
|
|
|
13
13
|
|
|
14
14
|
如果任一命令失败,按调用该规则的 skill 约定停止或降级。
|
|
15
15
|
|
|
16
|
+
## Upstream 仓库与权限检测
|
|
17
|
+
|
|
18
|
+
在后续任何 `gh issue` 或 `gh api "repos/..."` 操作之前,先按 `.agents/rules/issue-sync.md` 完成 `upstream_repo`、`has_triage` 和 `has_push` 检测。
|
|
19
|
+
|
|
20
|
+
- 后续所有 `gh issue` 命令统一使用 `-R "$upstream_repo"`
|
|
21
|
+
- 后续所有 repo 级 `gh api` 命令统一使用 `"repos/$upstream_repo/..."`
|
|
22
|
+
- `gh pr *` 命令保持作用于当前仓库,不额外加 `-R`
|
|
23
|
+
- `gh api "orgs/{owner}/..."` 这类 org 级命令保持不变
|
|
24
|
+
|
|
16
25
|
## Issue 读取与创建
|
|
17
26
|
|
|
18
27
|
读取 Issue:
|
|
19
28
|
|
|
20
29
|
```bash
|
|
21
|
-
gh issue view {issue-number} --json number,title,body,labels,state,milestone,url
|
|
30
|
+
gh issue view {issue-number} -R "$upstream_repo" --json number,title,body,labels,state,milestone,url
|
|
22
31
|
```
|
|
23
32
|
|
|
24
33
|
创建 Issue:
|
|
25
34
|
|
|
26
35
|
```bash
|
|
27
|
-
gh issue create --title "{title}" --body "{body}" --assignee @me {label-args} {milestone-arg}
|
|
36
|
+
gh issue create -R "$upstream_repo" --title "{title}" --body "{body}" --assignee @me {label-args} {milestone-arg}
|
|
28
37
|
```
|
|
29
38
|
|
|
30
39
|
- `{label-args}` 由调用方按有效 label 列表展开为多个 `--label`
|
|
40
|
+
- 仅当 `has_triage=true` 时传入 `{label-args}`;否则整体省略并继续
|
|
31
41
|
- 没有有效 label 时省略全部 `--label`
|
|
42
|
+
- 仅当 `has_triage=true` 时传入 `{milestone-arg}`;否则整体省略并继续
|
|
32
43
|
- `{milestone-arg}` 为空时整体省略
|
|
33
44
|
|
|
34
45
|
设置 Issue Type:
|
|
35
46
|
|
|
36
47
|
```bash
|
|
37
48
|
gh api "orgs/{owner}/issue-types" --jq '.[].name'
|
|
38
|
-
gh api "repos/
|
|
49
|
+
gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH -f type="{issue-type}" --silent
|
|
39
50
|
```
|
|
40
51
|
|
|
52
|
+
- 仅当 `has_push=true` 时执行 Issue Type 设置;否则跳过并继续
|
|
53
|
+
|
|
41
54
|
## Issue 更新
|
|
42
55
|
|
|
43
56
|
更新标题、label、assignee 或 milestone 时使用:
|
|
44
57
|
|
|
45
58
|
```bash
|
|
46
|
-
gh issue edit {issue-number} {edit-args}
|
|
59
|
+
gh issue edit {issue-number} -R "$upstream_repo" {edit-args}
|
|
47
60
|
```
|
|
48
61
|
|
|
49
62
|
常见参数:
|
|
50
63
|
- `--title "{title}"`
|
|
51
|
-
- `--add-label "{label}"`
|
|
52
|
-
- `--remove-label "{label}"`
|
|
64
|
+
- `--add-label "{label}"`(仅当 `has_triage=true`)
|
|
65
|
+
- `--remove-label "{label}"`(仅当 `has_triage=true`)
|
|
53
66
|
- `--add-assignee @me`
|
|
54
|
-
- `--milestone "{milestone}"`
|
|
67
|
+
- `--milestone "{milestone}"`(仅当 `has_triage=true`)
|
|
68
|
+
|
|
69
|
+
Assignee 同步不做权限预判;如果命令失败,按调用方约定静默跳过。
|
|
55
70
|
|
|
56
71
|
关闭 Issue:
|
|
57
72
|
|
|
58
73
|
```bash
|
|
59
|
-
gh issue close {issue-number} --reason "{reason}"
|
|
74
|
+
gh issue close {issue-number} -R "$upstream_repo" --reason "{reason}"
|
|
60
75
|
```
|
|
61
76
|
|
|
62
77
|
## Issue 评论读取
|
|
@@ -64,7 +79,7 @@ gh issue close {issue-number} --reason "{reason}"
|
|
|
64
79
|
读取 Issue 评论或按隐藏标记查找已有评论:
|
|
65
80
|
|
|
66
81
|
```bash
|
|
67
|
-
gh api "repos/
|
|
82
|
+
gh api "repos/$upstream_repo/issues/{issue-number}/comments" --paginate
|
|
68
83
|
```
|
|
69
84
|
|
|
70
85
|
## PR 读取与创建
|
|
@@ -84,12 +99,21 @@ gh pr list --state {state} --base {base-branch} --json number,title,url,headRefN
|
|
|
84
99
|
创建 PR:
|
|
85
100
|
|
|
86
101
|
```bash
|
|
87
|
-
gh pr create --base "{target-branch}" --title "{title}" --assignee @me
|
|
102
|
+
gh pr create --base "{target-branch}" --title "{title}" --assignee @me \
|
|
103
|
+
{label-args} {milestone-arg} \
|
|
104
|
+
--body "$(cat <<'EOF'
|
|
88
105
|
{pr-body}
|
|
89
106
|
EOF
|
|
90
107
|
)"
|
|
91
108
|
```
|
|
92
109
|
|
|
110
|
+
- `{label-args}` 由调用方按有效 label 列表展开为多个 `--label "{label}"`
|
|
111
|
+
- 仅当 `has_triage=true` 时传入 `{label-args}`;否则整体省略并继续
|
|
112
|
+
- 没有有效 label 时省略全部 `--label`
|
|
113
|
+
- `{milestone-arg}` 展开为 `--milestone "{milestone}"`
|
|
114
|
+
- 仅当 `has_triage=true` 时传入 `{milestone-arg}`;否则整体省略并继续
|
|
115
|
+
- `{milestone-arg}` 为空时整体省略
|
|
116
|
+
|
|
93
117
|
## PR 更新
|
|
94
118
|
|
|
95
119
|
更新 PR 标题、label 或 milestone:
|
|
@@ -108,4 +132,5 @@ gh pr edit {pr-number} {edit-args}
|
|
|
108
132
|
|
|
109
133
|
- 读取失败:按调用方规则决定停止还是跳过
|
|
110
134
|
- 更新失败:如果调用方标记为 best-effort,输出警告并继续
|
|
135
|
+
- 权限不足:按 `has_triage` / `has_push` 分支跳过直接写操作,不阻塞调用方
|
|
111
136
|
- `@me` 由 `gh` CLI 解析为当前认证用户
|
|
@@ -2,45 +2,121 @@
|
|
|
2
2
|
|
|
3
3
|
Read this file before a task skill updates a GitHub Issue.
|
|
4
4
|
|
|
5
|
+
## Upstream Repository Detection
|
|
6
|
+
|
|
7
|
+
When an external contributor runs `gh` inside a fork, the default target is the fork instead of the upstream repository. Detect the upstream repository first and reuse `upstream_repo` for every later `gh issue` and `gh api "repos/..."` operation.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
upstream_repo=$(gh api "repos/$(gh repo view --json nameWithOwner -q .nameWithOwner)" \
|
|
11
|
+
--jq 'if .fork then .parent.full_name else .full_name end' 2>/dev/null)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
- non-fork repository: returns the current repository `full_name`
|
|
15
|
+
- fork repository: returns the parent repository `full_name`
|
|
16
|
+
- every later `gh issue` command must use `-R "$upstream_repo"`
|
|
17
|
+
- every later `gh api "repos/..."` command must use `"repos/$upstream_repo/..."`
|
|
18
|
+
|
|
19
|
+
## Permission Detection
|
|
20
|
+
|
|
21
|
+
Run one permission check against the upstream repository before any write operation. When detection fails, treat it as no permission so the workflow degrades safely.
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
repo_perms=$(gh api "repos/$upstream_repo" --jq '.permissions' 2>/dev/null || echo '{}')
|
|
25
|
+
has_triage=$(printf '%s' "$repo_perms" | grep -q '"triage":true' 2>/dev/null && echo true || echo false)
|
|
26
|
+
has_push=$(printf '%s' "$repo_perms" | grep -q '"push":true' 2>/dev/null && echo true || echo false)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Operation-to-permission mapping:
|
|
30
|
+
|
|
31
|
+
| Operation | Required permission | Notes |
|
|
32
|
+
|------|---------|------|
|
|
33
|
+
| add/remove labels | `has_triage` | triage is the minimum permission |
|
|
34
|
+
| add/remove milestones | `has_triage` | same as above |
|
|
35
|
+
| edit Issue body | `has_triage` | used by requirement checkbox sync |
|
|
36
|
+
| set Issue Type | `has_push` | requires write permission |
|
|
37
|
+
| set assignee | no check | skip directly when it fails |
|
|
38
|
+
| publish/update comments | no check | allowed for authenticated users in public repositories |
|
|
39
|
+
|
|
40
|
+
## Degradation Rules
|
|
41
|
+
|
|
42
|
+
| Level | Operation type | With permission | Without permission |
|
|
43
|
+
|------|---------|--------|--------|
|
|
44
|
+
| silent degradation | label / milestone / Issue Type | run the `gh` command directly and also update the task comment | skip direct `gh` writes, update only the task comment, let the bot backfill |
|
|
45
|
+
| direct skip | assignee | run the `gh` command directly | do nothing else |
|
|
46
|
+
| normal execution | comments | run normally | run normally |
|
|
47
|
+
|
|
48
|
+
Key rules:
|
|
49
|
+
|
|
50
|
+
- task comment sync must continue whether write permission exists or not
|
|
51
|
+
- insufficient permission only affects direct Issue metadata writes and must not stop the skill
|
|
52
|
+
- keep the existing `2>/dev/null || true` error-tolerance pattern
|
|
53
|
+
|
|
54
|
+
## External Contributor Locking
|
|
55
|
+
|
|
56
|
+
Maintainers (`has_triage=true`) are never blocked. External contributors (`has_triage=false`) must check whether the current task already has a `task` comment author on the Issue before they start.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
task_comment_author=$(gh api "repos/$upstream_repo/issues/{issue-number}/comments" \
|
|
60
|
+
--paginate --jq '[.[] | select(.body | test("<!-- sync-issue:{task-id}:task -->")) | .user.login] | first' \
|
|
61
|
+
2>/dev/null || echo "")
|
|
62
|
+
current_user=$(gh api user --jq '.login' 2>/dev/null || echo "")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Decision rules:
|
|
66
|
+
|
|
67
|
+
- no `task` comment exists: allow execution
|
|
68
|
+
- the `task` comment author is the current user: allow continuation
|
|
69
|
+
- the `task` comment author is another user: stop immediately and ask the contributor to coordinate with a maintainer before taking over
|
|
70
|
+
|
|
5
71
|
## Direct `status:` Label Updates
|
|
6
72
|
|
|
7
|
-
|
|
73
|
+
Algorithm note: keep the flow below aligned with `.github/scripts/sync-labels-to-set.sh` (set-diff sync). This is the AI agent-side equivalent implementation for the `target_set = {"{target-status-label}"}` case. If either side changes, update the other one in the same patch to avoid drift between the agent and the bot.
|
|
74
|
+
|
|
75
|
+
If task.md contains a valid `issue_number` (not empty and not `N/A`) and the Issue state is `OPEN`, sync the `status:` labels to the target value with an idempotent set diff:
|
|
8
76
|
|
|
9
77
|
```bash
|
|
10
|
-
state=$(gh issue view {issue-number} --json state --jq '.state' 2>/dev/null)
|
|
78
|
+
state=$(gh issue view {issue-number} -R "$upstream_repo" --json state --jq '.state' 2>/dev/null)
|
|
11
79
|
if [ "$state" = "OPEN" ]; then
|
|
12
|
-
gh issue view {issue-number}
|
|
13
|
-
--jq '.labels[].name | select(startswith("status:"))' 2>/dev/null
|
|
14
|
-
| while IFS= read -r label; do
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
80
|
+
current_status_labels=$(gh issue view {issue-number} -R "$upstream_repo" \
|
|
81
|
+
--json labels --jq '.labels[].name | select(startswith("status:"))' 2>/dev/null || true)
|
|
82
|
+
printf '%s\n' "$current_status_labels" | while IFS= read -r label; do
|
|
83
|
+
[ -z "$label" ] && continue
|
|
84
|
+
if [ "$label" != "{target-status-label}" ] && [ "$has_triage" = "true" ]; then
|
|
85
|
+
gh issue edit {issue-number} -R "$upstream_repo" --remove-label "$label" 2>/dev/null || true
|
|
86
|
+
fi
|
|
87
|
+
done
|
|
88
|
+
if [ "$has_triage" = "true" ] && ! printf '%s\n' "$current_status_labels" | grep -qxF "{target-status-label}"; then
|
|
89
|
+
gh issue edit {issue-number} -R "$upstream_repo" --add-label "{target-status-label}" 2>/dev/null || true
|
|
90
|
+
fi
|
|
19
91
|
fi
|
|
20
92
|
```
|
|
21
93
|
|
|
22
94
|
Use `while IFS= read -r label` so labels like `status: in-progress` are handled line-by-line instead of being split on spaces.
|
|
23
95
|
|
|
96
|
+
If `has_triage=false`, skip direct label changes, update only the task comment, and let the bot backfill from the latest task metadata.
|
|
97
|
+
|
|
24
98
|
If `gh` fails, skip and continue. Do not fail the skill.
|
|
25
99
|
|
|
26
100
|
## Assignee Sync
|
|
27
101
|
|
|
28
102
|
When a skill creates or imports an Issue, automatically add the current executor as assignee:
|
|
29
103
|
|
|
30
|
-
- `create-issue`: use `--assignee @me` in
|
|
31
|
-
- `import-issue`: run `gh issue edit {issue-number} --add-assignee @me 2>/dev/null || true` after import
|
|
104
|
+
- `create-issue`: use `--assignee @me` in `gh issue create` and include `-R "$upstream_repo"`
|
|
105
|
+
- `import-issue`: run `gh issue edit {issue-number} -R "$upstream_repo" --add-assignee @me 2>/dev/null || true` after import
|
|
32
106
|
|
|
33
|
-
`@me` is resolved by `gh` CLI to the authenticated user. The operation is idempotent
|
|
107
|
+
`@me` is resolved by `gh` CLI to the authenticated user. The operation is idempotent. If the command fails, skip it directly and do not provide a fallback path.
|
|
34
108
|
|
|
35
109
|
## `in:` Label Sync
|
|
36
110
|
|
|
111
|
+
> **Trigger timing**: run `in:` label sync only after code is committed (the `commit` skill). Do not run it during `implement-task` or `refine-task`. During `create-pr`, only copy the labels from the Issue to the PR without recomputing them.
|
|
112
|
+
|
|
37
113
|
Read the `labels.in` mapping from `.agents/.airc.json`.
|
|
38
114
|
|
|
39
115
|
```bash
|
|
40
116
|
git diff {base-branch}...HEAD --name-only
|
|
41
117
|
```
|
|
42
118
|
|
|
43
|
-
`{base-branch}` is usually `main`; in PR context, use the PR
|
|
119
|
+
`{base-branch}` is usually `main`; in PR context, use the PR base branch.
|
|
44
120
|
|
|
45
121
|
### When a mapping exists (precise add/remove)
|
|
46
122
|
|
|
@@ -48,15 +124,18 @@ git diff {base-branch}...HEAD --name-only
|
|
|
48
124
|
2. Match each file against the directory prefixes in `labels.in` to compute the expected `in:` label set
|
|
49
125
|
3. Query the current `in:` labels on the Issue or PR
|
|
50
126
|
4. Apply the diff:
|
|
51
|
-
- expected but missing
|
|
52
|
-
- present but no longer expected
|
|
127
|
+
- expected but missing: only when `has_triage=true`, run `gh issue edit {issue-number} -R "$upstream_repo" --add-label "in: {module}" 2>/dev/null || true`
|
|
128
|
+
- present but no longer expected: only when `has_triage=true`, run `gh issue edit {issue-number} -R "$upstream_repo" --remove-label "in: {module}" 2>/dev/null || true`
|
|
53
129
|
|
|
54
130
|
### When no mapping exists (add-only fallback)
|
|
55
131
|
|
|
56
132
|
If `.airc.json` has no `labels.in` field or it is empty:
|
|
133
|
+
|
|
57
134
|
1. query existing repository `in:` labels
|
|
58
135
|
2. derive the top-level directory from each changed file
|
|
59
|
-
3. add matching labels
|
|
136
|
+
3. only when `has_triage=true`, add matching labels and never remove existing `in:` labels
|
|
137
|
+
|
|
138
|
+
If `has_triage=false`, skip direct `in:` label edits and keep task comment sync as the source for later automation backfill.
|
|
60
139
|
|
|
61
140
|
## Artifact Comment Publishing
|
|
62
141
|
|
|
@@ -69,7 +148,7 @@ The hidden marker must remain compatible:
|
|
|
69
148
|
Check for an existing comment before publishing:
|
|
70
149
|
|
|
71
150
|
```bash
|
|
72
|
-
gh api "repos/
|
|
151
|
+
gh api "repos/$upstream_repo/issues/{issue-number}/comments" \
|
|
73
152
|
--paginate --jq '.[].body' \
|
|
74
153
|
| grep -qF "<!-- sync-issue:{task-id}:{file-stem} -->"
|
|
75
154
|
```
|
|
@@ -99,20 +178,23 @@ Use this format:
|
|
|
99
178
|
`{agent}` is the name of the AI agent currently executing the skill (for example `claude`, `codex`, or `gemini`).
|
|
100
179
|
|
|
101
180
|
`summary` comments need extra handling:
|
|
181
|
+
|
|
102
182
|
- find an existing `<!-- sync-issue:{task-id}:summary -->` comment ID first
|
|
103
183
|
- create the comment when none exists
|
|
104
|
-
- patch the existing comment in place when the body changed
|
|
184
|
+
- patch the existing comment in place when the body changed by using `gh api "repos/$upstream_repo/issues/comments/{comment-id}" -X PATCH -f body=...`
|
|
105
185
|
|
|
106
186
|
```bash
|
|
107
|
-
summary_comment_id=$(gh api "repos/
|
|
187
|
+
summary_comment_id=$(gh api "repos/$upstream_repo/issues/{issue-number}/comments" \
|
|
108
188
|
--paginate --jq '.[] | select(.body | startswith("<!-- sync-issue:{task-id}:summary -->")) | .id' \
|
|
109
189
|
| head -n 1)
|
|
110
|
-
gh api "repos/
|
|
190
|
+
gh api "repos/$upstream_repo/issues/comments/{comment-id}" -X PATCH -f body="$(cat <<'EOF'
|
|
111
191
|
{comment-body}
|
|
112
192
|
EOF
|
|
113
193
|
)"
|
|
114
194
|
```
|
|
115
195
|
|
|
196
|
+
Comment publishing is not gated by `has_triage` or `has_push`.
|
|
197
|
+
|
|
116
198
|
## task.md Comment Sync
|
|
117
199
|
|
|
118
200
|
Hidden marker:
|
|
@@ -124,7 +206,7 @@ Hidden marker:
|
|
|
124
206
|
Use an idempotent update path for `task.md`:
|
|
125
207
|
|
|
126
208
|
1. Read the full `task.md`
|
|
127
|
-
2. Wrap the YAML frontmatter (content between the `---` delimiters) inside a `<details><summary>Metadata (frontmatter)</summary>` block with a `yaml` code fence
|
|
209
|
+
2. Wrap the YAML frontmatter (content between the `---` delimiters) inside a `<details><summary>Metadata (frontmatter)</summary>` block with a `yaml` code fence, then render the remaining body as normal Markdown
|
|
128
210
|
3. Use `task` as `{file-stem}`
|
|
129
211
|
4. Find an existing comment ID for the marker
|
|
130
212
|
5. Create the comment when none exists
|
|
@@ -155,20 +237,23 @@ task.md comment format:
|
|
|
155
237
|
*Generated by {agent} · Internal tracking: {task-id}*
|
|
156
238
|
```
|
|
157
239
|
|
|
158
|
-
When restoring, extract the frontmatter from the `<details>` block and reassemble with the body to recover the original `task.md`.
|
|
240
|
+
When restoring, extract the frontmatter from the `<details>` block and reassemble it with the body to recover the original `task.md`.
|
|
159
241
|
|
|
160
242
|
Title mapping:
|
|
243
|
+
|
|
161
244
|
- `task` -> `Task File`
|
|
162
245
|
|
|
246
|
+
task comment sync always runs and is never downgraded.
|
|
247
|
+
|
|
163
248
|
## Backfill Rules (run before `/complete-task` archives)
|
|
164
249
|
|
|
165
250
|
- Scan `task.md`, `analysis*.md`, `plan*.md`, `implementation*.md`, `review*.md`, and `refinement*.md` in the task directory
|
|
166
251
|
- Check whether each `{file-stem}` was already published by its hidden marker; publish only missing artifacts
|
|
167
|
-
- Backfill only appends missing comments
|
|
252
|
+
- Backfill only appends missing comments and never deletes or reorders existing comments
|
|
168
253
|
- Resolve `{agent}` for backfilled comments in this order:
|
|
169
|
-
1.
|
|
170
|
-
2.
|
|
171
|
-
3.
|
|
254
|
+
1. match the artifact filename in Activity Log (for example `→ analysis.md`) and extract the executor from `by {agent}`
|
|
255
|
+
2. if no match is found, fall back to `assigned_to` in task.md frontmatter
|
|
256
|
+
3. if `assigned_to` is also unavailable, use the current backfilling agent
|
|
172
257
|
- Derive the previous and next neighbors from Activity Log order and add this note below the title:
|
|
173
258
|
|
|
174
259
|
```markdown
|
|
@@ -178,6 +263,7 @@ Title mapping:
|
|
|
178
263
|
- If only one neighbor exists, keep only that side of the note; if neither exists, omit the note
|
|
179
264
|
|
|
180
265
|
Title mapping:
|
|
266
|
+
|
|
181
267
|
- `task` -> `Task File`
|
|
182
268
|
- `analysis` / `analysis-r{N}` -> `Requirements Analysis` / `Requirements Analysis (Round {N})`
|
|
183
269
|
- `plan` / `plan-r{N}` -> `Technical Plan` / `Technical Plan (Round {N})`
|
|
@@ -186,6 +272,8 @@ Title mapping:
|
|
|
186
272
|
- `refinement` / `refinement-r{N}` -> `Refinement Report (Round 1)` / `Refinement Report (Round {N})`
|
|
187
273
|
- `summary` -> `Delivery Summary`
|
|
188
274
|
|
|
275
|
+
Backfilled comments are also not gated by `has_triage` or `has_push`.
|
|
276
|
+
|
|
189
277
|
## Requirement Checkbox Sync
|
|
190
278
|
|
|
191
279
|
Extract checked `- [x]` items from the `## Requirements` section in task.md. Skip when none exist.
|
|
@@ -193,10 +281,12 @@ Extract checked `- [x]` items from the `## Requirements` section in task.md. Ski
|
|
|
193
281
|
Read the current Issue body:
|
|
194
282
|
|
|
195
283
|
```bash
|
|
196
|
-
gh issue view {issue-number} --json body --jq '.body'
|
|
284
|
+
gh issue view {issue-number} -R "$upstream_repo" --json body --jq '.body'
|
|
197
285
|
```
|
|
198
286
|
|
|
199
|
-
Replace matching `- [ ] {text}` lines with `- [x] {text}`
|
|
287
|
+
Replace matching `- [ ] {text}` lines with `- [x] {text}`. Use `gh api` to PATCH the full body only when the body changed and `has_triage=true`.
|
|
288
|
+
|
|
289
|
+
If `has_triage=false`, skip the body PATCH, update only the task comment, and let the bot backfill from the latest task state.
|
|
200
290
|
|
|
201
291
|
## Shell Safety Rules
|
|
202
292
|
|