@fitlab-ai/agent-infra 0.5.4 → 0.5.6
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 +94 -1
- package/README.zh-CN.md +94 -1
- package/lib/defaults.json +1 -0
- package/lib/sandbox/commands/create.js +7 -3
- package/lib/sandbox/shell.js +47 -7
- package/lib/sandbox/tools.js +18 -14
- package/package.json +1 -1
- package/templates/.agents/README.en.md +52 -0
- package/templates/.agents/README.zh-CN.md +52 -0
- package/templates/.agents/rules/issue-pr-commands.github.en.md +10 -1
- package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +10 -1
- package/templates/.agents/rules/issue-sync.github.en.md +12 -10
- package/templates/.agents/rules/issue-sync.github.zh-CN.md +12 -10
- package/templates/.agents/rules/milestone-inference.github.en.md +6 -5
- package/templates/.agents/rules/milestone-inference.github.zh-CN.md +6 -5
- package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +87 -14
- package/templates/.agents/skills/analyze-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/create-pr/config/verify.json +2 -0
- package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +6 -7
- package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +6 -7
- 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/implement-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/import-issue/SKILL.en.md +10 -2
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -2
- package/templates/.agents/skills/import-issue/config/verify.json +2 -1
- package/templates/.agents/skills/plan-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/refine-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/review-task/SKILL.en.md +1 -1
- package/templates/.agents/skills/review-task/SKILL.zh-CN.md +1 -1
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +316 -1
- package/templates/.github/scripts/sync-labels-to-set.sh +110 -0
- package/templates/.github/workflows/metadata-sync.yml +11 -20
- package/templates/.github/workflows/pr-label.yml +10 -19
- package/templates/.github/workflows/status-label.yml +20 -34
|
@@ -70,20 +70,22 @@ Decision rules:
|
|
|
70
70
|
|
|
71
71
|
## Direct `status:` Label Updates
|
|
72
72
|
|
|
73
|
-
|
|
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:
|
|
74
76
|
|
|
75
77
|
```bash
|
|
76
78
|
state=$(gh issue view {issue-number} -R "$upstream_repo" --json state --jq '.state' 2>/dev/null)
|
|
77
79
|
if [ "$state" = "OPEN" ]; then
|
|
78
|
-
gh issue view {issue-number} -R "$upstream_repo"
|
|
79
|
-
--jq '.labels[].name | select(startswith("status:"))' 2>/dev/null
|
|
80
|
-
| while IFS= read -r label; do
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if [ "$has_triage" = "true" ]; then
|
|
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
|
|
87
89
|
gh issue edit {issue-number} -R "$upstream_repo" --add-label "{target-status-label}" 2>/dev/null || true
|
|
88
90
|
fi
|
|
89
91
|
fi
|
|
@@ -70,20 +70,22 @@ current_user=$(gh api user --jq '.login' 2>/dev/null || echo "")
|
|
|
70
70
|
|
|
71
71
|
## status label 设置
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
算法说明:下面的流程与 `.github/scripts/sync-labels-to-set.sh` 保持一致(集合差集)。本章节是 AI Agent 侧的等价实现(`target_set = {"{target-status-label}"}` 的特例)。修改任一侧时,必须同步另一侧,避免 Agent 与 Bot 的行为漂移。
|
|
74
|
+
|
|
75
|
+
如果 task.md 中存在有效的 `issue_number`(非空、非 `N/A`),且 Issue 状态为 `OPEN`,则按幂等差集方式将 `status:` label 同步到目标值:
|
|
74
76
|
|
|
75
77
|
```bash
|
|
76
78
|
state=$(gh issue view {issue-number} -R "$upstream_repo" --json state --jq '.state' 2>/dev/null)
|
|
77
79
|
if [ "$state" = "OPEN" ]; then
|
|
78
|
-
gh issue view {issue-number} -R "$upstream_repo"
|
|
79
|
-
--jq '.labels[].name | select(startswith("status:"))' 2>/dev/null
|
|
80
|
-
| while IFS= read -r label; do
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if [ "$has_triage" = "true" ]; then
|
|
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
|
|
87
89
|
gh issue edit {issue-number} -R "$upstream_repo" --add-label "{target-status-label}" 2>/dev/null || true
|
|
88
90
|
fi
|
|
89
91
|
fi
|
|
@@ -50,17 +50,18 @@ Goal: narrow the Issue milestone from a release line to a concrete version when
|
|
|
50
50
|
|
|
51
51
|
Preconditions:
|
|
52
52
|
- task.md contains a valid `issue_number`
|
|
53
|
-
- the current Issue milestone matches the release-line format `X.Y.x`
|
|
53
|
+
- the current Issue milestone matches the release-line format `X.Y.x` or is `General Backlog`
|
|
54
54
|
|
|
55
55
|
Sequence:
|
|
56
56
|
1. Query the current Issue milestone
|
|
57
|
-
2. If it is
|
|
58
|
-
3. If it is in `X.Y.x` format,
|
|
57
|
+
2. If it is `General Backlog`, re-infer the release line using Phase 1, then try to narrow it to a concrete version; if inference fails, keep `General Backlog` unchanged
|
|
58
|
+
3. If it is not in `X.Y.x` format, treat it as already specific enough and keep it unchanged
|
|
59
|
+
4. If it is in `X.Y.x` format, narrow it according to branch mode:
|
|
59
60
|
- Trunk mode: query open concrete-version milestones on that release line (for example `0.4.4`) and choose the latest one
|
|
60
61
|
- Multi-version branch mode:
|
|
61
62
|
- If the task branch was created from `origin/X.Y.x`, choose the latest concrete version on that line
|
|
62
63
|
- If the task branch was created from `main`, find the highest release line and choose the latest concrete version on that line
|
|
63
|
-
|
|
64
|
+
5. When a target concrete version is found, run:
|
|
64
65
|
|
|
65
66
|
```bash
|
|
66
67
|
if [ "$has_triage" = "true" ]; then
|
|
@@ -68,7 +69,7 @@ if [ "$has_triage" = "true" ]; then
|
|
|
68
69
|
fi
|
|
69
70
|
```
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
6. If `has_triage=false`, the target milestone does not exist, or the branch ancestry cannot be determined reliably, keep the original milestone unchanged
|
|
72
73
|
|
|
73
74
|
Suggested concrete-version query:
|
|
74
75
|
|
|
@@ -50,17 +50,18 @@ Milestone 设置属于 `has_triage` 权限范围;如果调用方检测到 `has
|
|
|
50
50
|
|
|
51
51
|
前置条件:
|
|
52
52
|
- `task.md` 存在有效 `issue_number`
|
|
53
|
-
- 当前 Issue milestone 为版本线格式 `X.Y.x`
|
|
53
|
+
- 当前 Issue milestone 为版本线格式 `X.Y.x` 或 `General Backlog`
|
|
54
54
|
|
|
55
55
|
执行顺序:
|
|
56
56
|
1. 查询 Issue 当前 milestone
|
|
57
|
-
2. 如果 milestone
|
|
58
|
-
3. 如果 milestone
|
|
57
|
+
2. 如果 milestone 是 `General Backlog` -> 按阶段 1 规则重新推断版本线,再尝试收窄到具体版本;如果推断失败则保持 `General Backlog` 不变
|
|
58
|
+
3. 如果 milestone 不是 `X.Y.x` 格式 -> 视为已足够具体,保持不变
|
|
59
|
+
4. 如果 milestone 是 `X.Y.x` -> 按分支模式收窄:
|
|
59
60
|
- 主干模式:查询该版本线下 open 的具体版本 milestone(如 `0.4.4`),取最新版本
|
|
60
61
|
- 多版本分支模式:
|
|
61
62
|
- 当前任务分支来自 `origin/X.Y.x` release line -> 在该版本线下取最新具体版本
|
|
62
63
|
- 当前任务分支来自 `main` -> 找最高版本线,再取该版本线下的最新具体版本
|
|
63
|
-
|
|
64
|
+
5. 找到目标具体版本后,执行:
|
|
64
65
|
|
|
65
66
|
```bash
|
|
66
67
|
if [ "$has_triage" = "true" ]; then
|
|
@@ -68,7 +69,7 @@ if [ "$has_triage" = "true" ]; then
|
|
|
68
69
|
fi
|
|
69
70
|
```
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
6. 如果 `has_triage=false`、目标 milestone 不存在,或无法可靠判断 -> 保持原 milestone 不变
|
|
72
73
|
|
|
73
74
|
具体版本查询建议:
|
|
74
75
|
|
|
@@ -82,6 +82,7 @@ export function check({ taskDir, config, artifactFile }, shared) {
|
|
|
82
82
|
checkCommentContent,
|
|
83
83
|
checkTaskCommentContent,
|
|
84
84
|
checkInLabelsComputed,
|
|
85
|
+
checkPrTypeLabel,
|
|
85
86
|
checkInLabelsMatchPr,
|
|
86
87
|
checkPrAssignee,
|
|
87
88
|
checkSyncedRequirements,
|
|
@@ -251,10 +252,11 @@ function fetchRemoteData(context) {
|
|
|
251
252
|
let prMilestone;
|
|
252
253
|
let prAssignees;
|
|
253
254
|
if (((context.config.verify_in_labels_match_pr && context.hasTriage)
|
|
255
|
+
|| (context.config.verify_pr_type_label && context.hasTriage)
|
|
254
256
|
|| (context.config.verify_milestone && context.hasTriage)
|
|
255
257
|
|| (context.config.verify_pr_assignee && context.hasPush)) && context.prNumber) {
|
|
256
258
|
const prFields = [];
|
|
257
|
-
if (context.config.verify_in_labels_match_pr) {
|
|
259
|
+
if (context.config.verify_in_labels_match_pr || context.config.verify_pr_type_label) {
|
|
258
260
|
prFields.push("labels");
|
|
259
261
|
}
|
|
260
262
|
if (context.config.verify_milestone) {
|
|
@@ -280,7 +282,7 @@ function fetchRemoteData(context) {
|
|
|
280
282
|
};
|
|
281
283
|
}
|
|
282
284
|
|
|
283
|
-
prLabels = context.config.verify_in_labels_match_pr
|
|
285
|
+
prLabels = (context.config.verify_in_labels_match_pr || context.config.verify_pr_type_label)
|
|
284
286
|
? extractLabelNames(prResult.value?.labels)
|
|
285
287
|
: null;
|
|
286
288
|
prMilestone = context.config.verify_milestone
|
|
@@ -302,6 +304,22 @@ function fetchRemoteData(context) {
|
|
|
302
304
|
};
|
|
303
305
|
}
|
|
304
306
|
|
|
307
|
+
function mapTaskTypeToLabel(taskType) {
|
|
308
|
+
const mapping = {
|
|
309
|
+
bug: "type: bug",
|
|
310
|
+
bugfix: "type: bug",
|
|
311
|
+
feature: "type: feature",
|
|
312
|
+
enhancement: "type: enhancement",
|
|
313
|
+
refactor: "type: enhancement",
|
|
314
|
+
refactoring: "type: enhancement",
|
|
315
|
+
documentation: "type: documentation",
|
|
316
|
+
"dependency-upgrade": "type: dependency-upgrade",
|
|
317
|
+
task: "type: task"
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
return mapping[taskType] || null;
|
|
321
|
+
}
|
|
322
|
+
|
|
305
323
|
function shouldFetchComments(config) {
|
|
306
324
|
return Boolean(
|
|
307
325
|
config.expected_comment_marker
|
|
@@ -481,6 +499,26 @@ function checkTaskCommentContent(context, remoteData) {
|
|
|
481
499
|
);
|
|
482
500
|
}
|
|
483
501
|
|
|
502
|
+
function checkPrTypeLabel(context, remoteData) {
|
|
503
|
+
if (!context.config.verify_pr_type_label || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
|
|
504
|
+
return null;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
const expectedLabel = mapTaskTypeToLabel(context.task.metadata.type);
|
|
508
|
+
if (!expectedLabel) {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (remoteData.prLabels.includes(expectedLabel)) {
|
|
513
|
+
return null;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
return failResult(CHECK_TYPE,
|
|
517
|
+
`Expected type label '${expectedLabel}' not found on PR #${context.prNumber}`,
|
|
518
|
+
"check_failed"
|
|
519
|
+
);
|
|
520
|
+
}
|
|
521
|
+
|
|
484
522
|
function checkInLabelsMatchPr(context, remoteData) {
|
|
485
523
|
if (!context.config.verify_in_labels_match_pr || !context.hasTriage || !context.prNumber || !remoteData.prLabels) {
|
|
486
524
|
return null;
|
|
@@ -897,22 +935,22 @@ function resolveUpstreamRepo(taskDir) {
|
|
|
897
935
|
return ownerRepo;
|
|
898
936
|
}
|
|
899
937
|
|
|
900
|
-
const
|
|
938
|
+
const repoResult = ghJson([
|
|
901
939
|
"api",
|
|
902
|
-
`repos/${ownerRepo.value}
|
|
903
|
-
"--jq",
|
|
904
|
-
"if .fork then .parent.full_name else .full_name end"
|
|
940
|
+
`repos/${ownerRepo.value}`
|
|
905
941
|
], taskDir);
|
|
906
942
|
|
|
907
|
-
if (!
|
|
908
|
-
return
|
|
943
|
+
if (!repoResult.ok) {
|
|
944
|
+
return repoResult;
|
|
909
945
|
}
|
|
910
946
|
|
|
911
|
-
|
|
947
|
+
const repo = repoResult.value && typeof repoResult.value === "object" ? repoResult.value : {};
|
|
948
|
+
const upstreamRepo = repo.fork ? repo.parent?.full_name : repo.full_name;
|
|
949
|
+
if (isBlank(upstreamRepo)) {
|
|
912
950
|
return { ok: false, message: "Unable to resolve upstream repository" };
|
|
913
951
|
}
|
|
914
952
|
|
|
915
|
-
return { ok: true, value:
|
|
953
|
+
return { ok: true, value: upstreamRepo };
|
|
916
954
|
}
|
|
917
955
|
|
|
918
956
|
function resolveOwnerRepo(taskDir) {
|
|
@@ -979,11 +1017,12 @@ function ghText(args, cwd) {
|
|
|
979
1017
|
}
|
|
980
1018
|
|
|
981
1019
|
function ghCommand(args, cwd) {
|
|
982
|
-
const
|
|
1020
|
+
const command = resolveCommand("gh");
|
|
1021
|
+
const result = spawnSync(command, args, commandOptions(command, {
|
|
983
1022
|
cwd,
|
|
984
1023
|
encoding: "utf8",
|
|
985
1024
|
env: process.env
|
|
986
|
-
});
|
|
1025
|
+
}));
|
|
987
1026
|
|
|
988
1027
|
if (result.status !== 0) {
|
|
989
1028
|
const stderr = `${result.stderr || ""}${result.stdout || ""}`.trim();
|
|
@@ -999,11 +1038,12 @@ function ghPaginatedJson(args, cwd) {
|
|
|
999
1038
|
}
|
|
1000
1039
|
|
|
1001
1040
|
function gitText(args, cwd) {
|
|
1002
|
-
const
|
|
1041
|
+
const command = resolveCommand("git");
|
|
1042
|
+
const result = spawnSync(command, args, commandOptions(command, {
|
|
1003
1043
|
cwd,
|
|
1004
1044
|
encoding: "utf8",
|
|
1005
1045
|
env: process.env
|
|
1006
|
-
});
|
|
1046
|
+
}));
|
|
1007
1047
|
|
|
1008
1048
|
if (result.status !== 0) {
|
|
1009
1049
|
const stderr = `${result.stderr || ""}${result.stdout || ""}`.trim();
|
|
@@ -1072,6 +1112,39 @@ function sleep(delayMs) {
|
|
|
1072
1112
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delayMs);
|
|
1073
1113
|
}
|
|
1074
1114
|
|
|
1115
|
+
function resolveCommand(cmd) {
|
|
1116
|
+
if (process.platform !== "win32" || path.extname(cmd)) {
|
|
1117
|
+
return cmd;
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
const pathValue = process.env.Path || process.env.PATH || "";
|
|
1121
|
+
const extensions = (process.env.PATHEXT || ".COM;.EXE;.BAT;.CMD")
|
|
1122
|
+
.split(";")
|
|
1123
|
+
.filter(Boolean);
|
|
1124
|
+
|
|
1125
|
+
for (const dir of pathValue.split(path.delimiter).filter(Boolean)) {
|
|
1126
|
+
for (const extension of extensions) {
|
|
1127
|
+
const lowerCandidate = path.join(dir, `${cmd}${extension.toLowerCase()}`);
|
|
1128
|
+
if (fs.existsSync(lowerCandidate)) {
|
|
1129
|
+
return lowerCandidate;
|
|
1130
|
+
}
|
|
1131
|
+
const upperCandidate = path.join(dir, `${cmd}${extension.toUpperCase()}`);
|
|
1132
|
+
if (fs.existsSync(upperCandidate)) {
|
|
1133
|
+
return upperCandidate;
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
return cmd;
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
function commandOptions(cmd, options) {
|
|
1142
|
+
if (process.platform === "win32" && /\.(?:bat|cmd)$/i.test(cmd)) {
|
|
1143
|
+
return { ...options, shell: true };
|
|
1144
|
+
}
|
|
1145
|
+
return options;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1075
1148
|
function interpolate(template, taskDir, artifactFile) {
|
|
1076
1149
|
const artifactStem = artifactFile ? path.basename(artifactFile, path.extname(artifactFile)) : "";
|
|
1077
1150
|
return template
|
|
@@ -121,8 +121,8 @@ Update `.agents/workspace/active/{task-id}/task.md`:
|
|
|
121
121
|
If task.md contains a valid `issue_number`, perform these sync actions (skip and continue on any failure):
|
|
122
122
|
- Read `.agents/rules/issue-sync.md` before syncing, and complete upstream repository detection plus permission detection
|
|
123
123
|
- Set `status: pending-design-work` by following issue-sync.md
|
|
124
|
-
- Publish the `{analysis-artifact}` comment
|
|
125
124
|
- Create or update the `<!-- sync-issue:{task-id}:task -->` comment (follow the task.md comment sync rule in issue-sync.md)
|
|
125
|
+
- Publish the `{analysis-artifact}` comment
|
|
126
126
|
|
|
127
127
|
### 7. Verification Gate
|
|
128
128
|
|
|
@@ -121,8 +121,8 @@ date "+%Y-%m-%d %H:%M:%S%:z"
|
|
|
121
121
|
如果 task.md 中存在有效的 `issue_number`,执行以下同步操作(任一失败则跳过并继续):
|
|
122
122
|
- 执行前先读取 `.agents/rules/issue-sync.md`,完成 upstream 仓库检测和权限检测
|
|
123
123
|
- 按 issue-sync.md 设置 `status: pending-design-work`
|
|
124
|
-
- 发布 `{analysis-artifact}` 评论
|
|
125
124
|
- 创建或更新 `<!-- sync-issue:{task-id}:task -->` 评论(按 issue-sync.md 的 task.md 评论同步规则)
|
|
125
|
+
- 发布 `{analysis-artifact}` 评论
|
|
126
126
|
|
|
127
127
|
### 7. 完成校验
|
|
128
128
|
|
|
@@ -27,7 +27,7 @@ git diff <target-branch>...HEAD
|
|
|
27
27
|
|
|
28
28
|
Read `.agents/rules/issue-pr-commands.md` before this step.
|
|
29
29
|
|
|
30
|
-
Before syncing linked Issue metadata, complete authentication and code-hosting platform detection through that rule. Keep `gh pr list` / `gh pr
|
|
30
|
+
Before syncing linked Issue metadata, complete authentication and code-hosting platform detection through that rule. Keep `gh pr list` / `gh pr create` on the current repository.
|
|
31
31
|
|
|
32
32
|
Before syncing labels, verify the standard label system:
|
|
33
33
|
|
|
@@ -52,13 +52,12 @@ Type label mapping:
|
|
|
52
52
|
|
|
53
53
|
Metadata sync order:
|
|
54
54
|
1. query Issue labels and milestone via the Issue read command in `.agents/rules/issue-pr-commands.md`
|
|
55
|
-
2.
|
|
56
|
-
3.
|
|
57
|
-
4.
|
|
58
|
-
5.
|
|
59
|
-
6. ensure the PR body contains `Closes #{issue-number}` or an equivalent closing keyword
|
|
55
|
+
2. build `{label-args}` from the mapped type label, non-`type:` / non-`status:` Issue labels, and the current Issue `in:` labels (commit already computed them, so do not recompute them here and do not write back to the Issue)
|
|
56
|
+
3. build `{milestone-arg}` by following "Phase 3: `create-pr`" in `.agents/rules/milestone-inference.md` and reusing the Issue milestone directly
|
|
57
|
+
4. pass `{label-args}` and `{milestone-arg}` atomically to `gh pr create` by using the create-PR command template and permission-degradation rules in `.agents/rules/issue-pr-commands.md`
|
|
58
|
+
5. ensure the PR body contains `Closes #{issue-number}` or an equivalent closing keyword
|
|
60
59
|
|
|
61
|
-
If those rules say to skip the direct metadata
|
|
60
|
+
If those rules say to skip the direct metadata arguments above, keep only the PR body linkage plus later comment sync.
|
|
62
61
|
|
|
63
62
|
Milestone rule:
|
|
64
63
|
- Follow "Phase 3: `create-pr`" in `.agents/rules/milestone-inference.md`
|
|
@@ -27,7 +27,7 @@ git diff <target-branch>...HEAD
|
|
|
27
27
|
|
|
28
28
|
执行前先读取 `.agents/rules/issue-pr-commands.md`。
|
|
29
29
|
|
|
30
|
-
同步关联 Issue 元数据前,先按该规则完成认证和代码托管平台检测;`gh pr list` / `gh pr
|
|
30
|
+
同步关联 Issue 元数据前,先按该规则完成认证和代码托管平台检测;`gh pr list` / `gh pr create` 仍保持作用于当前仓库。
|
|
31
31
|
|
|
32
32
|
在同步 label 之前,先确认标准 label 体系已经存在:
|
|
33
33
|
|
|
@@ -52,13 +52,12 @@ Type label 映射:
|
|
|
52
52
|
|
|
53
53
|
元数据同步顺序:
|
|
54
54
|
1. 按 `.agents/rules/issue-pr-commands.md` 的 Issue 读取命令查询关联 Issue 的 labels 和 milestone
|
|
55
|
-
2.
|
|
56
|
-
3.
|
|
57
|
-
4.
|
|
58
|
-
5.
|
|
59
|
-
6. 确保 PR 正文包含 `Closes #{issue-number}` 或等价的 closing keyword
|
|
55
|
+
2. 构建 `{label-args}`:包含映射后的 type label、非 `type:`/`status:` 的 Issue labels,以及 Issue 当前的 `in:` labels(commit 阶段已完成计算,此处不重新计算也不反向更新 Issue)
|
|
56
|
+
3. 构建 `{milestone-arg}`:按 `.agents/rules/milestone-inference.md` 的「阶段 3:`create-pr`」直接复用 Issue milestone
|
|
57
|
+
4. 按 `.agents/rules/issue-pr-commands.md` 的创建 PR 命令模板与权限降级规则,将 `{label-args}` 和 `{milestone-arg}` 原子化传入 `gh pr create`
|
|
58
|
+
5. 确保 PR 正文包含 `Closes #{issue-number}` 或等价的 closing keyword
|
|
60
59
|
|
|
61
|
-
|
|
60
|
+
如果上述规则判定应跳过直接元数据参数写入,则只保留 PR 正文中的关联信息与后续评论同步。
|
|
62
61
|
|
|
63
62
|
Milestone 规则:
|
|
64
63
|
- 按 `.agents/rules/milestone-inference.md` 的「阶段 3:`create-pr`」处理
|
|
@@ -54,7 +54,7 @@ Read `.agents/rules/release-commands.md` before this step.
|
|
|
54
54
|
- When generating release notes in Step 7, **must** follow both the historical format style and the full category list gathered in Step 3
|
|
55
55
|
- If no historical release notes exist, use the default format defined in Step 7
|
|
56
56
|
|
|
57
|
-
### 4. Collect Merged PRs
|
|
57
|
+
### 4. Collect Merged PRs and Contributors
|
|
58
58
|
|
|
59
59
|
Get the date range between tags, then query merged PRs:
|
|
60
60
|
|
|
@@ -71,6 +71,17 @@ Also collect direct commits without PRs:
|
|
|
71
71
|
git log v<prev-version>..v<version> --format="%H %s" --no-merges
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
Collect collaborative contributors from commit `Co-authored-by` trailers:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git log v<prev-version>..v<version> \
|
|
78
|
+
--no-merges \
|
|
79
|
+
--format='%(trailers:key=Co-authored-by,valueonly,unfold)' \
|
|
80
|
+
| grep -v '^$' | sort | uniq -c | sort -rn
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Each output line is `Name <email>` and `uniq -c` provides the number of commits where that identity appeared as a co-author within the range.
|
|
84
|
+
|
|
74
85
|
### 5. Collect Related Issues
|
|
75
86
|
|
|
76
87
|
From each PR body, extract linked Issues:
|
|
@@ -115,7 +126,21 @@ If no historical release notes exist, use the following default Markdown format:
|
|
|
115
126
|
1. Item format: `- [scope] Description by @author in [#N](url)`
|
|
116
127
|
2. Issue + PR: `in [#Issue](url) and [#PR](url)`
|
|
117
128
|
3. Description: Use PR title, remove `type(scope):` prefix, capitalize first letter
|
|
118
|
-
4.
|
|
129
|
+
4. **Contributor collection**:
|
|
130
|
+
- **Data sources**:
|
|
131
|
+
- PR authors from Step 4 `gh pr list --json author`
|
|
132
|
+
- Commit co-authors from Step 4 `git log ... --format='%(trailers:key=Co-authored-by,valueonly,unfold)'`
|
|
133
|
+
- **Contribution count**: `PR count + co-authored commit count` for the same identity, merged across both sources
|
|
134
|
+
- **Name -> `@login` mapping**:
|
|
135
|
+
- Raw `Co-authored-by` values are `Name <email>` and must be mapped to a GitHub `@login`
|
|
136
|
+
- Prefer email extraction: if it matches `(\d+\+)?(\S+?)@users\.noreply\.github\.com`, use the second capture group lowercased; this regex covers both `{id}+{login}@users.noreply.github.com` and `{login}@users.noreply.github.com`
|
|
137
|
+
- Otherwise use a Name heuristic: take the first token before a space and lowercase it, for example `Claude Opus 4.6 (1M context)` -> `@claude`, `Codex` -> `@codex`, `Gemini` -> `@gemini`
|
|
138
|
+
- If the login already appears in the PR author list, merge counts into that login so `Claude` and `@claude` do not become separate entries
|
|
139
|
+
- Merge all Name variants that map to the same login before counting and sorting; for example, `Claude` and `Claude Opus 4.6 (1M context)` should both collapse into `@claude`
|
|
140
|
+
- Preserve bot identities as-is, for example `dependabot[bot]`
|
|
141
|
+
- If the login still cannot be determined reliably, output `@{lowercased first Name token}` and append `<!-- TODO(reviewer): confirm GitHub login for {original Name <email>} -->` below the `Contributors` section
|
|
142
|
+
- **Sorting**: descending by contribution count, then lexicographically by login for ties
|
|
143
|
+
- **Deduplication**: use the final mapped `@login` as the key
|
|
119
144
|
5. Empty sections: Omit sections with no entries
|
|
120
145
|
|
|
121
146
|
### 8. Present and Confirm
|
|
@@ -54,7 +54,7 @@ git rev-parse v<prev-version>
|
|
|
54
54
|
- 后续步骤 7 生成发布说明时,**必须**同时参考步骤 3 的历史格式风格和完整分类清单,保持版本间的一致性
|
|
55
55
|
- 如果没有历史发布说明,则使用步骤 7 中定义的默认格式
|
|
56
56
|
|
|
57
|
-
### 4. 收集已合并的 PR
|
|
57
|
+
### 4. 收集已合并的 PR 与贡献者
|
|
58
58
|
|
|
59
59
|
获取标签之间的日期范围,然后查询已合并的 PR:
|
|
60
60
|
|
|
@@ -71,6 +71,17 @@ git log v<version> --format=%aI -1
|
|
|
71
71
|
git log v<prev-version>..v<version> --format="%H %s" --no-merges
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
从 commit `Co-authored-by` trailer 中收集协作贡献者:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
git log v<prev-version>..v<version> \
|
|
78
|
+
--no-merges \
|
|
79
|
+
--format='%(trailers:key=Co-authored-by,valueonly,unfold)' \
|
|
80
|
+
| grep -v '^$' | sort | uniq -c | sort -rn
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
输出每行一个 `Name <email>`(`uniq -c` 给出该身份在范围内作为 co-author 的 commit 数)。
|
|
84
|
+
|
|
74
85
|
### 5. 收集关联 Issue
|
|
75
86
|
|
|
76
87
|
从每个 PR body 中提取关联的 Issue:
|
|
@@ -115,7 +126,21 @@ git log v<prev-version>..v<version> --format="%H %s" --no-merges
|
|
|
115
126
|
1. 条目格式:`- [scope] Description by @author in [#N](url)`
|
|
116
127
|
2. Issue + PR:`in [#Issue](url) and [#PR](url)`
|
|
117
128
|
3. 描述:使用 PR 标题,移除 `type(scope):` 前缀,首字母大写
|
|
118
|
-
4.
|
|
129
|
+
4. **贡献者搜集**:
|
|
130
|
+
- **数据源**:
|
|
131
|
+
- PR author:来自步骤 4 的 `gh pr list --json author`
|
|
132
|
+
- Commit co-authors:来自步骤 4 的 `git log ... --format='%(trailers:key=Co-authored-by,valueonly,unfold)'`
|
|
133
|
+
- **贡献数定义**:`该人的 PR 数 + 该人作为 co-author 的 commit 数`(同一身份跨来源合并计数)
|
|
134
|
+
- **Name → `@login` 映射**:
|
|
135
|
+
- `Co-authored-by` 原始格式为 `Name <email>`,需要推断对应的 GitHub `@login`
|
|
136
|
+
- 优先从 email 提取:匹配 `(\d+\+)?(\S+?)@users\.noreply\.github\.com` 时,取第二个捕获组并转为小写;该正则同时覆盖 `{id}+{login}@users.noreply.github.com` 与 `{login}@users.noreply.github.com`
|
|
137
|
+
- 否则按 Name 启发式:取首个空格前的 token 并转为小写(例如 `Claude Opus 4.6 (1M context)` → `@claude`、`Codex` → `@codex`、`Gemini` → `@gemini`)
|
|
138
|
+
- 已出现在 PR author 列表中的 login,必须按该 login 合并计数,避免把 `Claude` 和 `@claude` 拆成两个条目
|
|
139
|
+
- 同一 login 的所有 Name 变体都必须归并后再计数与排序;例如 `Claude` 与 `Claude Opus 4.6 (1M context)` 都映射到 `@claude` 时,应先合并为同一个贡献者
|
|
140
|
+
- Bot 身份保留原样(如 `dependabot[bot]`)
|
|
141
|
+
- 若仍无法可靠确定 login,则输出 `@{Name 首 token 小写}`,并在 `Contributors` 段落下追加 `<!-- TODO(reviewer): 确认 {原始 Name <email>} 的 GitHub login -->`
|
|
142
|
+
- **排序**:按贡献数降序;贡献数相同时按 login 字典序
|
|
143
|
+
- **去重**:以最终映射后的 `@login` 为键
|
|
119
144
|
5. 空部分:省略没有条目的部分
|
|
120
145
|
|
|
121
146
|
### 8. 展示并确认
|
|
@@ -95,8 +95,8 @@ Update `.agents/workspace/active/{task-id}/task.md`:
|
|
|
95
95
|
|
|
96
96
|
If task.md contains a valid `issue_number`, perform these sync actions (skip and continue on any failure; read `.agents/rules/issue-sync.md` first and complete upstream repository detection plus permission detection):
|
|
97
97
|
- Set `status: in-progress` by following issue-sync.md
|
|
98
|
-
- Publish the `{implementation-artifact}` comment
|
|
99
98
|
- Create or update the `<!-- sync-issue:{task-id}:task -->` comment (follow the task.md comment sync rule in issue-sync.md)
|
|
99
|
+
- Publish the `{implementation-artifact}` comment
|
|
100
100
|
|
|
101
101
|
### 10. Verification Gate
|
|
102
102
|
|
|
@@ -95,8 +95,8 @@ date "+%Y-%m-%d %H:%M:%S%:z"
|
|
|
95
95
|
|
|
96
96
|
如果 task.md 中存在有效的 `issue_number`,执行以下同步操作(任一失败则跳过并继续;执行前先读取 `.agents/rules/issue-sync.md`,完成 upstream 仓库检测和权限检测):
|
|
97
97
|
- 按 issue-sync.md 设置 `status: in-progress`
|
|
98
|
-
- 发布 `{implementation-artifact}` 评论
|
|
99
98
|
- 创建或更新 `<!-- sync-issue:{task-id}:task -->` 评论(按 issue-sync.md 的 task.md 评论同步规则)
|
|
99
|
+
- 发布 `{implementation-artifact}` 评论
|
|
100
100
|
|
|
101
101
|
### 10. 完成校验
|
|
102
102
|
|
|
@@ -74,7 +74,14 @@ Update `.agents/workspace/active/{task-id}/task.md`:
|
|
|
74
74
|
|
|
75
75
|
If task.md contains a valid `issue_number`, use the Issue update command from `.agents/rules/issue-pr-commands.md` to add the current executor as an assignee. The behavioral boundary still follows `.agents/rules/issue-sync.md`.
|
|
76
76
|
|
|
77
|
-
### 6.
|
|
77
|
+
### 6. Sync to the Issue
|
|
78
|
+
|
|
79
|
+
If task.md contains a valid `issue_number`, perform these sync actions (skip and continue on any failure):
|
|
80
|
+
- Read `.agents/rules/issue-sync.md` before syncing, and complete upstream repository detection plus permission detection
|
|
81
|
+
- Check the Issue's current milestone; if it is unset, read `.agents/rules/milestone-inference.md` and infer plus set the milestone using "Stage 1: `create-issue`". If `has_triage=false` or the inference is uncertain, skip and continue
|
|
82
|
+
- Create or update the `<!-- sync-issue:{task-id}:task -->` comment (follow the task.md comment sync rule in issue-sync.md)
|
|
83
|
+
|
|
84
|
+
### 7. Verification Gate
|
|
78
85
|
|
|
79
86
|
Run the verification gate to confirm the task artifact and sync state are valid:
|
|
80
87
|
|
|
@@ -89,7 +96,7 @@ Handle the result as follows:
|
|
|
89
96
|
|
|
90
97
|
Keep the gate output in your reply as fresh evidence. Do not claim completion without output from this run.
|
|
91
98
|
|
|
92
|
-
###
|
|
99
|
+
### 8. Inform User
|
|
93
100
|
|
|
94
101
|
> Execute this step only after the verification gate passes.
|
|
95
102
|
|
|
@@ -119,6 +126,7 @@ Next step - run requirements analysis:
|
|
|
119
126
|
- [ ] Updated `current_step` to requirement-analysis
|
|
120
127
|
- [ ] Updated `updated_at` to the current time
|
|
121
128
|
- [ ] Appended an Activity Log entry to task.md
|
|
129
|
+
- [ ] Synced the task comment to the Issue
|
|
122
130
|
- [ ] Informed the user of the next step (must include all TUI command formats; do not filter)
|
|
123
131
|
- [ ] **Did not modify any business code**
|
|
124
132
|
|
|
@@ -74,7 +74,14 @@ date "+%Y-%m-%d %H:%M:%S%:z"
|
|
|
74
74
|
|
|
75
75
|
如果 task.md 中存在有效的 `issue_number`,按 `.agents/rules/issue-pr-commands.md` 的 Issue 更新命令为当前执行者添加 assignee;Assignee 同步的边界仍遵循 `.agents/rules/issue-sync.md`。
|
|
76
76
|
|
|
77
|
-
### 6.
|
|
77
|
+
### 6. 同步到 Issue
|
|
78
|
+
|
|
79
|
+
如果 task.md 中存在有效的 `issue_number`,执行以下同步操作(任一失败则跳过并继续):
|
|
80
|
+
- 执行前先读取 `.agents/rules/issue-sync.md`,完成 upstream 仓库检测和权限检测
|
|
81
|
+
- 检查 Issue 当前 milestone;如果未设置,先读取 `.agents/rules/milestone-inference.md`,按其中的「阶段 1:`create-issue`」规则推断并设置 milestone;如果 `has_triage=false` 或推断不确定,跳过并继续
|
|
82
|
+
- 创建或更新 `<!-- sync-issue:{task-id}:task -->` 评论(按 issue-sync.md 的 task.md 评论同步规则)
|
|
83
|
+
|
|
84
|
+
### 7. 完成校验
|
|
78
85
|
|
|
79
86
|
运行完成校验,确认任务产物和同步状态符合规范:
|
|
80
87
|
|
|
@@ -89,7 +96,7 @@ node .agents/scripts/validate-artifact.js gate import-issue .agents/workspace/ac
|
|
|
89
96
|
|
|
90
97
|
将校验输出保留在回复中作为当次验证输出。没有当次校验输出,不得声明完成。
|
|
91
98
|
|
|
92
|
-
###
|
|
99
|
+
### 8. 告知用户
|
|
93
100
|
|
|
94
101
|
> 仅在校验通过后执行本步骤。
|
|
95
102
|
|
|
@@ -119,6 +126,7 @@ Issue #{number} 已导入。
|
|
|
119
126
|
- [ ] 更新了 `current_step` 为 requirement-analysis
|
|
120
127
|
- [ ] 更新了 `updated_at` 为当前时间
|
|
121
128
|
- [ ] 追加了 Activity Log 条目到 task.md
|
|
129
|
+
- [ ] 同步了 task 评论到 Issue
|
|
122
130
|
- [ ] 告知了用户下一步(必须展示所有 TUI 的命令格式,不要筛选)
|
|
123
131
|
- [ ] **没有修改任何业务代码**
|
|
124
132
|
|
|
@@ -99,8 +99,8 @@ Update `.agents/workspace/active/{task-id}/task.md`:
|
|
|
99
99
|
If task.md contains a valid `issue_number`, perform these sync actions (skip and continue on any failure):
|
|
100
100
|
- Read `.agents/rules/issue-sync.md` before syncing, and complete upstream repository detection plus permission detection
|
|
101
101
|
- Set `status: pending-design-work` by following issue-sync.md
|
|
102
|
-
- Publish the `{plan-artifact}` comment
|
|
103
102
|
- Create or update the `<!-- sync-issue:{task-id}:task -->` comment (follow the task.md comment sync rule in issue-sync.md)
|
|
103
|
+
- Publish the `{plan-artifact}` comment
|
|
104
104
|
|
|
105
105
|
### 8. Verification Gate
|
|
106
106
|
|
|
@@ -99,8 +99,8 @@ date "+%Y-%m-%d %H:%M:%S%:z"
|
|
|
99
99
|
如果 task.md 中存在有效的 `issue_number`,执行以下同步操作(任一失败则跳过并继续):
|
|
100
100
|
- 执行前先读取 `.agents/rules/issue-sync.md`,完成 upstream 仓库检测和权限检测
|
|
101
101
|
- 按 issue-sync.md 设置 `status: pending-design-work`
|
|
102
|
-
- 发布 `{plan-artifact}` 评论
|
|
103
102
|
- 创建或更新 `<!-- sync-issue:{task-id}:task -->` 评论(按 issue-sync.md 的 task.md 评论同步规则)
|
|
103
|
+
- 发布 `{plan-artifact}` 评论
|
|
104
104
|
|
|
105
105
|
### 8. 完成校验
|
|
106
106
|
|
|
@@ -62,8 +62,8 @@ Update task.md:
|
|
|
62
62
|
If task.md contains a valid `issue_number`, perform these sync actions (skip and continue on any failure):
|
|
63
63
|
- Read `.agents/rules/issue-sync.md` before syncing, and complete upstream repository detection plus permission detection
|
|
64
64
|
- Set `status: in-progress` by following issue-sync.md
|
|
65
|
-
- Publish the `{refinement-artifact}` comment
|
|
66
65
|
- Create or update the `<!-- sync-issue:{task-id}:task -->` comment (follow the task.md comment sync rule in issue-sync.md)
|
|
66
|
+
- Publish the `{refinement-artifact}` comment
|
|
67
67
|
|
|
68
68
|
### 7. Verification Gate
|
|
69
69
|
|