@fitlab-ai/agent-infra 0.4.4 → 0.5.0
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 +16 -2
- package/README.zh-CN.md +16 -2
- package/bin/cli.js +19 -0
- package/lib/defaults.json +17 -0
- package/lib/init.js +1 -0
- package/lib/log.js +5 -10
- package/lib/merge.js +465 -0
- package/lib/sandbox/commands/create.js +1047 -0
- package/lib/sandbox/commands/enter.js +31 -0
- package/lib/sandbox/commands/ls.js +70 -0
- package/lib/sandbox/commands/rebuild.js +102 -0
- package/lib/sandbox/commands/rm.js +211 -0
- package/lib/sandbox/commands/vm.js +101 -0
- package/lib/sandbox/config.js +79 -0
- package/lib/sandbox/constants.js +113 -0
- package/lib/sandbox/dockerfile.js +95 -0
- package/lib/sandbox/engine.js +93 -0
- package/lib/sandbox/index.js +64 -0
- package/lib/sandbox/runtimes/ai-tools.dockerfile +26 -0
- package/lib/sandbox/runtimes/base.dockerfile +30 -0
- package/lib/sandbox/runtimes/java17.dockerfile +3 -0
- package/lib/sandbox/runtimes/java21.dockerfile +3 -0
- package/lib/sandbox/runtimes/node20.dockerfile +3 -0
- package/lib/sandbox/runtimes/node22.dockerfile +3 -0
- package/lib/sandbox/runtimes/python3.dockerfile +3 -0
- package/lib/sandbox/shell.js +48 -0
- package/lib/sandbox/task-resolver.js +35 -0
- package/lib/sandbox/tools.js +131 -0
- package/lib/update.js +16 -2
- package/package.json +5 -1
- package/templates/.agents/rules/commit-and-pr.md +30 -0
- package/templates/.agents/rules/commit-and-pr.zh-CN.md +30 -0
- package/templates/.agents/rules/issue-sync.md +12 -2
- package/templates/.agents/rules/issue-sync.zh-CN.md +12 -2
- package/templates/.agents/rules/task-management.md +28 -0
- package/templates/.agents/rules/task-management.zh-CN.md +28 -0
- package/templates/.agents/scripts/validate-artifact.js +40 -0
- package/templates/.agents/skills/archive-tasks/SKILL.md +6 -3
- package/templates/.agents/skills/archive-tasks/SKILL.zh-CN.md +6 -3
- package/templates/.agents/skills/archive-tasks/scripts/archive-tasks.sh +91 -8
- package/templates/.agents/skills/create-task/SKILL.md +6 -0
- package/templates/.agents/skills/create-task/SKILL.zh-CN.md +6 -0
- package/templates/.agents/skills/create-task/config/verify.json +1 -0
- package/templates/.agents/skills/import-issue/SKILL.md +2 -0
- package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +2 -0
- package/templates/.agents/skills/import-issue/config/verify.json +1 -0
- package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +18 -1
- package/templates/.agents/templates/task.md +5 -4
- package/templates/.agents/templates/task.zh-CN.md +5 -4
|
@@ -88,12 +88,16 @@ Use this format:
|
|
|
88
88
|
<!-- sync-issue:{task-id}:{file-stem} -->
|
|
89
89
|
## {artifact-title}
|
|
90
90
|
|
|
91
|
+
> **{agent}** · {task-id}
|
|
92
|
+
|
|
91
93
|
{artifact body}
|
|
92
94
|
|
|
93
95
|
---
|
|
94
|
-
*Generated by
|
|
96
|
+
*Generated by {agent} · Internal tracking: {task-id}*
|
|
95
97
|
```
|
|
96
98
|
|
|
99
|
+
`{agent}` is the name of the AI agent currently executing the skill (for example `claude`, `codex`, or `gemini`).
|
|
100
|
+
|
|
97
101
|
`summary` comments need extra handling:
|
|
98
102
|
- find an existing `<!-- sync-issue:{task-id}:summary -->` comment ID first
|
|
99
103
|
- create the comment when none exists
|
|
@@ -133,6 +137,8 @@ task.md comment format:
|
|
|
133
137
|
<!-- sync-issue:{task-id}:task -->
|
|
134
138
|
## Task File
|
|
135
139
|
|
|
140
|
+
> **{agent}** · {task-id}
|
|
141
|
+
|
|
136
142
|
<details><summary>Metadata (frontmatter)</summary>
|
|
137
143
|
|
|
138
144
|
```yaml
|
|
@@ -146,7 +152,7 @@ task.md comment format:
|
|
|
146
152
|
{task.md body after frontmatter}
|
|
147
153
|
|
|
148
154
|
---
|
|
149
|
-
*Generated by
|
|
155
|
+
*Generated by {agent} · Internal tracking: {task-id}*
|
|
150
156
|
```
|
|
151
157
|
|
|
152
158
|
When restoring, extract the frontmatter from the `<details>` block and reassemble with the body to recover the original `task.md`.
|
|
@@ -159,6 +165,10 @@ Title mapping:
|
|
|
159
165
|
- Scan `task.md`, `analysis*.md`, `plan*.md`, `implementation*.md`, `review*.md`, and `refinement*.md` in the task directory
|
|
160
166
|
- Check whether each `{file-stem}` was already published by its hidden marker; publish only missing artifacts
|
|
161
167
|
- Backfill only appends missing comments; never delete or reorder existing comments
|
|
168
|
+
- Resolve `{agent}` for backfilled comments in this order:
|
|
169
|
+
1. Match the artifact filename in Activity Log (for example `→ analysis.md`) and extract the executor from `by {agent}`
|
|
170
|
+
2. If no match is found, fall back to `assigned_to` in task.md frontmatter
|
|
171
|
+
3. If `assigned_to` is also unavailable, use the current backfilling agent
|
|
162
172
|
- Derive the previous and next neighbors from Activity Log order and add this note below the title:
|
|
163
173
|
|
|
164
174
|
```markdown
|
|
@@ -88,12 +88,16 @@ gh api "repos/{owner}/{repo}/issues/{issue-number}/comments" \
|
|
|
88
88
|
<!-- sync-issue:{task-id}:{file-stem} -->
|
|
89
89
|
## {artifact-title}
|
|
90
90
|
|
|
91
|
+
> **{agent}** · {task-id}
|
|
92
|
+
|
|
91
93
|
{artifact body}
|
|
92
94
|
|
|
93
95
|
---
|
|
94
|
-
*由
|
|
96
|
+
*由 {agent} 自动生成 · 内部追踪:{task-id}*
|
|
95
97
|
```
|
|
96
98
|
|
|
99
|
+
其中 `{agent}` 使用当前执行该技能的 AI 代理名称(如 `claude`、`codex`、`gemini`)。
|
|
100
|
+
|
|
97
101
|
`summary` 评论需要额外处理:
|
|
98
102
|
- 先查找已有 `<!-- sync-issue:{task-id}:summary -->` 评论的 ID
|
|
99
103
|
- 不存在则创建
|
|
@@ -133,6 +137,8 @@ task.md 评论格式:
|
|
|
133
137
|
<!-- sync-issue:{task-id}:task -->
|
|
134
138
|
## 任务文件
|
|
135
139
|
|
|
140
|
+
> **{agent}** · {task-id}
|
|
141
|
+
|
|
136
142
|
<details><summary>元数据 (frontmatter)</summary>
|
|
137
143
|
|
|
138
144
|
```yaml
|
|
@@ -146,7 +152,7 @@ task.md 评论格式:
|
|
|
146
152
|
{task.md body after frontmatter}
|
|
147
153
|
|
|
148
154
|
---
|
|
149
|
-
*由
|
|
155
|
+
*由 {agent} 自动生成 · 内部追踪:{task-id}*
|
|
150
156
|
```
|
|
151
157
|
|
|
152
158
|
还原时,从 `<details>` 块中提取 frontmatter,与正文拼合恢复为原始 `task.md`。
|
|
@@ -159,6 +165,10 @@ task.md 评论格式:
|
|
|
159
165
|
- 扫描任务目录中的 `task.md`、`analysis*.md`、`plan*.md`、`implementation*.md`、`review*.md`、`refinement*.md`
|
|
160
166
|
- 对每个 `{file-stem}` 用隐藏标记检查是否已发布;未发布则补发,已发布则跳过
|
|
161
167
|
- 补发只追加缺失评论,不删除或重排已有评论
|
|
168
|
+
- 补发评论的 `{agent}` 按以下顺序确定:
|
|
169
|
+
1. 从 Activity Log 中匹配对应产物文件名(如 `→ analysis.md`),提取 `by {agent}` 中的执行者
|
|
170
|
+
2. 若未匹配到,则回退到 task.md frontmatter 的 `assigned_to`
|
|
171
|
+
3. 若 `assigned_to` 也不可用,则使用当前执行补发的 agent
|
|
162
172
|
- 位置说明从 Activity Log 推导时间线中的前后邻居,并加在评论标题下方:
|
|
163
173
|
|
|
164
174
|
```markdown
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# General Rules - Task Management
|
|
2
|
+
|
|
3
|
+
## Task Intent Detection
|
|
4
|
+
|
|
5
|
+
Map user intent to the corresponding workflow command:
|
|
6
|
+
- "analyze issue #123" -> `import-issue`
|
|
7
|
+
- "analyze task TASK-20260306-143022" -> `analyze-task`
|
|
8
|
+
- "design a plan" -> `plan-task`
|
|
9
|
+
- "implement" or "build" -> `implement-task`
|
|
10
|
+
- "review" -> `review-task`
|
|
11
|
+
- "fix review feedback" -> `refine-task`
|
|
12
|
+
|
|
13
|
+
## Task State Management
|
|
14
|
+
|
|
15
|
+
- Update the corresponding `task.md` immediately after every workflow command
|
|
16
|
+
- At minimum, synchronize `current_step`, `updated_at`, `assigned_to`, and the current-round artifact reference
|
|
17
|
+
- Activity Log entries are append-only and must never overwrite history
|
|
18
|
+
|
|
19
|
+
## Required State Updates by Command
|
|
20
|
+
|
|
21
|
+
- `import-issue`: update `current_step`, `updated_at`, `assigned_to`
|
|
22
|
+
- `analyze-task`: update `current_step`, `updated_at`, `assigned_to`
|
|
23
|
+
- `plan-task`: update `current_step`, `updated_at`
|
|
24
|
+
- `implement-task`: update `current_step`, `updated_at`
|
|
25
|
+
- `review-task`: update `current_step`, `updated_at`
|
|
26
|
+
- `refine-task`: update `current_step`, `updated_at`
|
|
27
|
+
- `complete-task`: update `status`, `completed_at`, `updated_at`
|
|
28
|
+
- `block-task`: update `status`, `blocked_at`, `blocked_reason`
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 通用规则 - 任务管理
|
|
2
|
+
|
|
3
|
+
## 任务语义识别
|
|
4
|
+
|
|
5
|
+
根据用户意图自动映射到对应工作流命令:
|
|
6
|
+
- “分析 issue #123” -> `import-issue`
|
|
7
|
+
- “分析任务 TASK-20260306-143022” -> `analyze-task`
|
|
8
|
+
- “设计方案” -> `plan-task`
|
|
9
|
+
- “实施/实现” -> `implement-task`
|
|
10
|
+
- “审查” -> `review-task`
|
|
11
|
+
- “修复审查问题” -> `refine-task`
|
|
12
|
+
|
|
13
|
+
## 任务状态管理
|
|
14
|
+
|
|
15
|
+
- 每次执行工作流命令后,必须立即更新对应任务的 `task.md`
|
|
16
|
+
- 至少同步 `current_step`、`updated_at`、`assigned_to`,以及本轮产物引用
|
|
17
|
+
- Activity Log 只能追加,不能覆盖历史记录
|
|
18
|
+
|
|
19
|
+
## 常见命令的状态更新要求
|
|
20
|
+
|
|
21
|
+
- `import-issue`:更新 `current_step`、`updated_at`、`assigned_to`
|
|
22
|
+
- `analyze-task`:更新 `current_step`、`updated_at`、`assigned_to`
|
|
23
|
+
- `plan-task`:更新 `current_step`、`updated_at`
|
|
24
|
+
- `implement-task`:更新 `current_step`、`updated_at`
|
|
25
|
+
- `review-task`:更新 `current_step`、`updated_at`
|
|
26
|
+
- `refine-task`:更新 `current_step`、`updated_at`
|
|
27
|
+
- `complete-task`:更新 `status`、`completed_at`、`updated_at`
|
|
28
|
+
- `block-task`:更新 `status`、`blocked_at`、`blocked_reason`
|
|
@@ -33,6 +33,7 @@ const DEFAULT_RETRY_DELAYS_MS = [3000, 10000];
|
|
|
33
33
|
const DEFAULT_FRESHNESS_MINUTES = 30;
|
|
34
34
|
const DATE_TIME_PATTERN = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
|
|
35
35
|
const ACTIVITY_LOG_PATTERN = /^- (\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) — \*\*(.+?)\*\* by (.+?) — (.+)$/;
|
|
36
|
+
const BRANCH_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
36
37
|
|
|
37
38
|
const scriptPath = fileURLToPath(import.meta.url);
|
|
38
39
|
const repoRoot = path.resolve(path.dirname(scriptPath), "..", "..");
|
|
@@ -190,6 +191,11 @@ function checkTaskMeta({ taskDir, config }) {
|
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
193
|
|
|
194
|
+
const branchValidationError = validateTaskBranch(metadata);
|
|
195
|
+
if (branchValidationError) {
|
|
196
|
+
return failResult("task-meta", branchValidationError);
|
|
197
|
+
}
|
|
198
|
+
|
|
193
199
|
const expectedStep = config.expected_step;
|
|
194
200
|
if (expectedStep && metadata.current_step !== expectedStep) {
|
|
195
201
|
return failResult(
|
|
@@ -232,6 +238,40 @@ function checkTaskMeta({ taskDir, config }) {
|
|
|
232
238
|
return passResult("task-meta", `Task metadata valid (${requiredFields.length} required fields checked)`);
|
|
233
239
|
}
|
|
234
240
|
|
|
241
|
+
function validateTaskBranch(metadata) {
|
|
242
|
+
if (isBlank(metadata.branch)) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const projectName = loadProjectName();
|
|
247
|
+
const expectedPrefix = projectName ? `${projectName}-${metadata.type}-` : "";
|
|
248
|
+
|
|
249
|
+
if (expectedPrefix && !String(metadata.branch).startsWith(expectedPrefix)) {
|
|
250
|
+
return `Invalid branch: expected prefix '${expectedPrefix}', got '${metadata.branch}'`;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const slug = expectedPrefix ? String(metadata.branch).slice(expectedPrefix.length) : String(metadata.branch);
|
|
254
|
+
if (!BRANCH_SLUG_PATTERN.test(slug)) {
|
|
255
|
+
return `Invalid branch: '${metadata.branch}' must use kebab-case suffixes`;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function loadProjectName() {
|
|
262
|
+
const configPath = path.join(repoRoot, ".agents", ".airc.json");
|
|
263
|
+
if (!fs.existsSync(configPath)) {
|
|
264
|
+
return "";
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
269
|
+
return String(config.project || "").trim();
|
|
270
|
+
} catch {
|
|
271
|
+
return "";
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
235
275
|
function checkArtifact({ taskDir, config, artifactFile }) {
|
|
236
276
|
const resolvedArtifact = resolveArtifactPath(taskDir, config.file_pattern, artifactFile);
|
|
237
277
|
if (!resolvedArtifact.ok) {
|
|
@@ -5,7 +5,10 @@ description: "Archive completed tasks into a date-organized workspace directory"
|
|
|
5
5
|
|
|
6
6
|
# Archive Completed Tasks
|
|
7
7
|
|
|
8
|
-
Move completed tasks from `.agents/workspace/completed/` into `.agents/workspace/archive/YYYY/MM/DD/TASK-xxx/` and rebuild
|
|
8
|
+
Move completed tasks from `.agents/workspace/completed/` into `.agents/workspace/archive/YYYY/MM/DD/TASK-xxx/` and rebuild a three-level archive index:
|
|
9
|
+
- root manifest: `.agents/workspace/archive/manifest.md`
|
|
10
|
+
- yearly manifest: `.agents/workspace/archive/YYYY/manifest.md`
|
|
11
|
+
- monthly manifest: `.agents/workspace/archive/YYYY/MM/manifest.md`
|
|
9
12
|
|
|
10
13
|
## Execution Flow
|
|
11
14
|
|
|
@@ -29,7 +32,7 @@ The script is responsible for:
|
|
|
29
32
|
- reading `completed_at` from `task.md` frontmatter and falling back to `updated_at`
|
|
30
33
|
- moving task directories directly into `YYYY/MM/DD/TASK-xxx/` without compression
|
|
31
34
|
- skipping already archived, missing, or malformed tasks
|
|
32
|
-
- rebuilding
|
|
35
|
+
- rebuilding root, yearly, and monthly manifests from all archived tasks
|
|
33
36
|
- printing an archive and skip summary
|
|
34
37
|
|
|
35
38
|
### 3. Inform the user
|
|
@@ -37,4 +40,4 @@ The script is responsible for:
|
|
|
37
40
|
Report:
|
|
38
41
|
- how many tasks were archived
|
|
39
42
|
- how many tasks were skipped and why
|
|
40
|
-
- the path to
|
|
43
|
+
- the path to the root manifest
|
|
@@ -5,7 +5,10 @@ description: "归档已完成任务到按日期组织的目录"
|
|
|
5
5
|
|
|
6
6
|
# 归档已完成任务
|
|
7
7
|
|
|
8
|
-
将 `.agents/workspace/completed/` 中的已完成任务移动到 `.agents/workspace/archive/YYYY/MM/DD/TASK-xxx
|
|
8
|
+
将 `.agents/workspace/completed/` 中的已完成任务移动到 `.agents/workspace/archive/YYYY/MM/DD/TASK-xxx/`,并重建三级归档索引:
|
|
9
|
+
- 根 manifest:`.agents/workspace/archive/manifest.md`
|
|
10
|
+
- 年 manifest:`.agents/workspace/archive/YYYY/manifest.md`
|
|
11
|
+
- 月 manifest:`.agents/workspace/archive/YYYY/MM/manifest.md`
|
|
9
12
|
|
|
10
13
|
## 执行流程
|
|
11
14
|
|
|
@@ -29,7 +32,7 @@ bash .agents/skills/archive-tasks/scripts/archive-tasks.sh [--days N | --before
|
|
|
29
32
|
- 解析 `task.md` frontmatter 中的 `completed_at`(缺失时回退到 `updated_at`)
|
|
30
33
|
- 按 `YYYY/MM/DD/TASK-xxx/` 目录直接移动任务,不压缩
|
|
31
34
|
- 跳过已归档、缺少元数据或不存在的任务
|
|
32
|
-
-
|
|
35
|
+
- 全量重建根 / 年 / 月三级 manifest
|
|
33
36
|
- 输出归档与跳过摘要
|
|
34
37
|
|
|
35
38
|
### 3. 告知用户
|
|
@@ -37,4 +40,4 @@ bash .agents/skills/archive-tasks/scripts/archive-tasks.sh [--days N | --before
|
|
|
37
40
|
向用户汇报:
|
|
38
41
|
- 本次归档的任务数量
|
|
39
42
|
- 跳过的任务数量和原因
|
|
40
|
-
-
|
|
43
|
+
- 根 manifest 的路径
|
|
@@ -208,6 +208,8 @@ should_archive_filtered_task() {
|
|
|
208
208
|
|
|
209
209
|
rebuild_manifest() {
|
|
210
210
|
entries_file="$tmpdir/manifest.tsv"
|
|
211
|
+
month_keys_file="$tmpdir/manifest-months.tsv"
|
|
212
|
+
year_keys_file="$tmpdir/manifest-years.tsv"
|
|
211
213
|
generated_at=$(date "+%Y-%m-%d %H:%M:%S")
|
|
212
214
|
|
|
213
215
|
mkdir -p "$ARCHIVE_DIR"
|
|
@@ -253,26 +255,107 @@ rebuild_manifest() {
|
|
|
253
255
|
fi
|
|
254
256
|
fi
|
|
255
257
|
|
|
256
|
-
printf '%s\t%s\t%s\t%s\t%s\n' "$completed_at" "$task_id" "$title" "$task_type" "$relative_path" >> "$entries_file"
|
|
258
|
+
printf '%s\t%s\t%s\t%s\t%s\t%s\t%s\n' "$year" "$month" "$completed_at" "$task_id" "$title" "$task_type" "$relative_path" >> "$entries_file"
|
|
257
259
|
done
|
|
258
260
|
done
|
|
259
261
|
done
|
|
260
262
|
done
|
|
261
263
|
|
|
264
|
+
find "$ARCHIVE_DIR" -type f -name 'manifest.md' -exec rm -f {} \;
|
|
265
|
+
|
|
266
|
+
awk -F'\t' '{print $1 "\t" $2}' "$entries_file" | LC_ALL=C sort -u > "$month_keys_file"
|
|
267
|
+
awk -F'\t' '{print $1}' "$entries_file" | LC_ALL=C sort -u > "$year_keys_file"
|
|
268
|
+
|
|
269
|
+
while IFS="$(printf '\t')" read -r year month; do
|
|
270
|
+
[ -n "$year" ] || continue
|
|
271
|
+
[ -n "$month" ] || continue
|
|
272
|
+
|
|
273
|
+
month_entries_file="$tmpdir/manifest-${year}-${month}.tsv"
|
|
274
|
+
month_manifest_path="$ARCHIVE_DIR/$year/$month/manifest.md"
|
|
275
|
+
|
|
276
|
+
awk -F'\t' -v target_year="$year" -v target_month="$month" '
|
|
277
|
+
$1 == target_year && $2 == target_month {
|
|
278
|
+
print $3 "\t" $4 "\t" $5 "\t" $6 "\t" $7
|
|
279
|
+
}
|
|
280
|
+
' "$entries_file" | LC_ALL=C sort -r > "$month_entries_file"
|
|
281
|
+
|
|
282
|
+
month_entry_count=$(wc -l < "$month_entries_file" | tr -d ' ')
|
|
283
|
+
|
|
284
|
+
{
|
|
285
|
+
echo "# Archive Manifest"
|
|
286
|
+
echo
|
|
287
|
+
echo "> Auto-generated by archive-tasks. Do not edit manually."
|
|
288
|
+
echo "> Last updated: $generated_at"
|
|
289
|
+
echo
|
|
290
|
+
echo "| Task ID | Title | Type | Completed | Path |"
|
|
291
|
+
echo "| --- | --- | --- | --- | --- |"
|
|
292
|
+
|
|
293
|
+
head -n 1000 "$month_entries_file" | while IFS="$(printf '\t')" read -r completed_at task_id title task_type relative_path; do
|
|
294
|
+
[ -n "$task_id" ] || continue
|
|
295
|
+
printf '| %s | %s | %s | %s | %s |\n' "$task_id" "$title" "$task_type" "$completed_at" "$relative_path"
|
|
296
|
+
done
|
|
297
|
+
|
|
298
|
+
if [ "$month_entry_count" -gt 1000 ]; then
|
|
299
|
+
echo
|
|
300
|
+
printf '> Showing 1000 of %s entries.\n' "$month_entry_count"
|
|
301
|
+
fi
|
|
302
|
+
} > "$month_manifest_path"
|
|
303
|
+
done < "$month_keys_file"
|
|
304
|
+
|
|
305
|
+
while IFS= read -r year; do
|
|
306
|
+
[ -n "$year" ] || continue
|
|
307
|
+
|
|
308
|
+
year_manifest_path="$ARCHIVE_DIR/$year/manifest.md"
|
|
309
|
+
|
|
310
|
+
{
|
|
311
|
+
echo "# Archive Manifest"
|
|
312
|
+
echo
|
|
313
|
+
echo "> Auto-generated by archive-tasks. Do not edit manually."
|
|
314
|
+
echo "> Last updated: $generated_at"
|
|
315
|
+
echo
|
|
316
|
+
echo "| Month | Tasks | Manifest |"
|
|
317
|
+
echo "| --- | --- | --- |"
|
|
318
|
+
|
|
319
|
+
awk -F'\t' -v target_year="$year" '
|
|
320
|
+
$1 == target_year {
|
|
321
|
+
counts[$2] += 1
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
END {
|
|
325
|
+
for (month in counts) {
|
|
326
|
+
print month "\t" counts[month]
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
' "$entries_file" | LC_ALL=C sort -r | while IFS="$(printf '\t')" read -r month task_count; do
|
|
330
|
+
[ -n "$month" ] || continue
|
|
331
|
+
printf '| %s | %s | [%s/manifest.md](%s/manifest.md) |\n' "$month" "$task_count" "$month" "$month"
|
|
332
|
+
done
|
|
333
|
+
} > "$year_manifest_path"
|
|
334
|
+
done < "$year_keys_file"
|
|
335
|
+
|
|
262
336
|
{
|
|
263
337
|
echo "# Archive Manifest"
|
|
264
338
|
echo
|
|
265
339
|
echo "> Auto-generated by archive-tasks. Do not edit manually."
|
|
266
340
|
echo "> Last updated: $generated_at"
|
|
267
341
|
echo
|
|
268
|
-
echo "|
|
|
269
|
-
echo "| --- | --- | --- |
|
|
342
|
+
echo "| Year | Tasks | Manifest |"
|
|
343
|
+
echo "| --- | --- | --- |"
|
|
270
344
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
345
|
+
awk -F'\t' '
|
|
346
|
+
{
|
|
347
|
+
counts[$1] += 1
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
END {
|
|
351
|
+
for (year in counts) {
|
|
352
|
+
print year "\t" counts[year]
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
' "$entries_file" | LC_ALL=C sort -r | while IFS="$(printf '\t')" read -r year task_count; do
|
|
356
|
+
[ -n "$year" ] || continue
|
|
357
|
+
printf '| %s | %s | [%s/manifest.md](%s/manifest.md) |\n' "$year" "$task_count" "$year" "$year"
|
|
358
|
+
done
|
|
276
359
|
} > "$MANIFEST_PATH"
|
|
277
360
|
}
|
|
278
361
|
|
|
@@ -27,6 +27,10 @@ Extract from the natural-language description:
|
|
|
27
27
|
- **Task title**: a concise title (maximum 50 characters), in the same language as the user's description - do not translate it to English or apply Conventional Commits formatting
|
|
28
28
|
- **Task type**: `feature` | `bugfix` | `refactor` | `docs` | `chore` (infer from the description)
|
|
29
29
|
- **Workflow**: `feature-development` | `bug-fix` | `refactoring` (infer from the type)
|
|
30
|
+
- **Branch name**: format `<project>-<type>-<slug>`
|
|
31
|
+
- `<project>` comes from the `project` field in `.agents/.airc.json`
|
|
32
|
+
- `<type>` is the inferred task type
|
|
33
|
+
- `<slug>` is a kebab-case slug built from 3-6 English keywords extracted from the task title
|
|
30
34
|
- **Detailed description**: the cleaned-up original user request
|
|
31
35
|
|
|
32
36
|
If the description is unclear, **ask the user to clarify first**.
|
|
@@ -64,6 +68,7 @@ Task metadata (`task.md` YAML front matter):
|
|
|
64
68
|
```yaml
|
|
65
69
|
id: TASK-{yyyyMMdd-HHmmss}
|
|
66
70
|
type: feature|bugfix|refactor|docs|chore
|
|
71
|
+
branch: <project>-<type>-<slug>
|
|
67
72
|
workflow: feature-development|bug-fix|refactoring
|
|
68
73
|
status: active
|
|
69
74
|
created_at: {yyyy-MM-dd HH:mm:ss}
|
|
@@ -87,6 +92,7 @@ Update `.agents/workspace/active/{task-id}/task.md`:
|
|
|
87
92
|
- `current_step`: requirement-analysis
|
|
88
93
|
- `assigned_to`: {current AI agent}
|
|
89
94
|
- `updated_at`: {current time}
|
|
95
|
+
- `## Context` -> `- **Branch**:`: update it to the generated branch name
|
|
90
96
|
- **Append** to `## Activity Log` (do NOT overwrite previous entries):
|
|
91
97
|
```
|
|
92
98
|
- {yyyy-MM-dd HH:mm:ss} — **Task Created** by {agent} — Task created from description
|
|
@@ -27,6 +27,10 @@ description: "根据自然语言描述创建任务"
|
|
|
27
27
|
- **任务标题**:简洁标题(最多 50 个字符),使用中文——不要翻译为英文,不要套用 Conventional Commits 格式
|
|
28
28
|
- **任务类型**:`feature` | `bugfix` | `refactor` | `docs` | `chore`(从描述推断)
|
|
29
29
|
- **工作流**:`feature-development` | `bug-fix` | `refactoring`(从类型推断)
|
|
30
|
+
- **分支名**:格式 `<project>-<type>-<slug>`
|
|
31
|
+
- `<project>` 从 `.agents/.airc.json` 的 `project` 字段读取
|
|
32
|
+
- `<type>` 为推断出的任务类型
|
|
33
|
+
- `<slug>` 从任务标题提取 3-6 个英文关键词并转为 kebab-case
|
|
30
34
|
- **详细描述**:整理后的用户原始描述
|
|
31
35
|
|
|
32
36
|
如果描述不清晰,**先向用户确认**再继续。
|
|
@@ -64,6 +68,7 @@ date +%Y%m%d-%H%M%S
|
|
|
64
68
|
```yaml
|
|
65
69
|
id: TASK-{yyyyMMdd-HHmmss}
|
|
66
70
|
type: feature|bugfix|refactor|docs|chore
|
|
71
|
+
branch: <project>-<type>-<slug>
|
|
67
72
|
workflow: feature-development|bug-fix|refactoring
|
|
68
73
|
status: active
|
|
69
74
|
created_at: {yyyy-MM-dd HH:mm:ss}
|
|
@@ -87,6 +92,7 @@ date "+%Y-%m-%d %H:%M:%S"
|
|
|
87
92
|
- `current_step`:requirement-analysis
|
|
88
93
|
- `assigned_to`:{当前 AI 代理}
|
|
89
94
|
- `updated_at`:{当前时间}
|
|
95
|
+
- `## 上下文` 中的 `- **分支**:`:更新为生成的分支名
|
|
90
96
|
- **追加**到 `## Activity Log`(不要覆盖之前的记录):
|
|
91
97
|
```
|
|
92
98
|
- {yyyy-MM-dd HH:mm:ss} — **Task Created** by {agent} — Task created from description
|
|
@@ -44,6 +44,7 @@ Task metadata:
|
|
|
44
44
|
id: TASK-{yyyyMMdd-HHmmss}
|
|
45
45
|
issue_number: <issue-number>
|
|
46
46
|
type: feature|bugfix|refactor|docs|chore
|
|
47
|
+
branch: <project>-<type>-<slug>
|
|
47
48
|
workflow: feature-development|bug-fix|refactoring
|
|
48
49
|
status: active
|
|
49
50
|
created_at: {yyyy-MM-dd HH:mm:ss}
|
|
@@ -65,6 +66,7 @@ Update `.agents/workspace/active/{task-id}/task.md`:
|
|
|
65
66
|
- `current_step`: requirement-analysis
|
|
66
67
|
- `assigned_to`: {current AI agent}
|
|
67
68
|
- `updated_at`: {current time}
|
|
69
|
+
- `## Context` -> `- **Branch**:`: update it to the generated branch name
|
|
68
70
|
- **Append** to `## Activity Log` (do NOT overwrite previous entries):
|
|
69
71
|
```
|
|
70
72
|
- {yyyy-MM-dd HH:mm:ss} — **Import Issue** by {agent} — Issue #{number} imported
|
|
@@ -44,6 +44,7 @@ date +%Y%m%d-%H%M%S
|
|
|
44
44
|
id: TASK-{yyyyMMdd-HHmmss}
|
|
45
45
|
issue_number: <issue-number>
|
|
46
46
|
type: feature|bugfix|refactor|docs|chore
|
|
47
|
+
branch: <project>-<type>-<slug>
|
|
47
48
|
workflow: feature-development|bug-fix|refactoring
|
|
48
49
|
status: active
|
|
49
50
|
created_at: {yyyy-MM-dd HH:mm:ss}
|
|
@@ -65,6 +66,7 @@ date "+%Y-%m-%d %H:%M:%S"
|
|
|
65
66
|
- `current_step`:requirement-analysis
|
|
66
67
|
- `assigned_to`:{当前 AI 代理}
|
|
67
68
|
- `updated_at`:{当前时间}
|
|
69
|
+
- `## 上下文` 中的 `- **分支**:`:更新为生成的分支名
|
|
68
70
|
- **追加**到 `## Activity Log`(不要覆盖之前的记录):
|
|
69
71
|
```
|
|
70
72
|
- {yyyy-MM-dd HH:mm:ss} — **Import Issue** by {agent} — Issue #{number} imported
|
|
@@ -20,6 +20,23 @@ import path from 'node:path';
|
|
|
20
20
|
import { fileURLToPath } from 'node:url';
|
|
21
21
|
|
|
22
22
|
const DEFAULTS = {
|
|
23
|
+
"sandbox": {
|
|
24
|
+
"runtimes": [
|
|
25
|
+
"node20"
|
|
26
|
+
],
|
|
27
|
+
"tools": [
|
|
28
|
+
"claude-code",
|
|
29
|
+
"codex",
|
|
30
|
+
"opencode",
|
|
31
|
+
"gemini-cli"
|
|
32
|
+
],
|
|
33
|
+
"dockerfile": null,
|
|
34
|
+
"vm": {
|
|
35
|
+
"cpu": null,
|
|
36
|
+
"memory": null,
|
|
37
|
+
"disk": null
|
|
38
|
+
}
|
|
39
|
+
},
|
|
23
40
|
"labels": {
|
|
24
41
|
"in": {}
|
|
25
42
|
},
|
|
@@ -57,7 +74,7 @@ const DEFAULTS = {
|
|
|
57
74
|
}
|
|
58
75
|
};
|
|
59
76
|
|
|
60
|
-
const INSTALLER_VERSION = "v0.
|
|
77
|
+
const INSTALLER_VERSION = "v0.5.0";
|
|
61
78
|
|
|
62
79
|
function norm(p) { return p.replace(/\\/g, '/'); }
|
|
63
80
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: task-XXX
|
|
3
|
-
type: feature
|
|
3
|
+
type: feature # feature | bugfix | refactor | docs | review
|
|
4
|
+
branch: "" # <project>-<type>-<slug>
|
|
4
5
|
workflow: feature-development # feature-development | bug-fix | code-review | refactoring
|
|
5
|
-
status: open
|
|
6
|
+
status: open # open | in-progress | review | blocked | completed
|
|
6
7
|
created_at: YYYY-MM-DD
|
|
7
8
|
updated_at: YYYY-MM-DD
|
|
8
|
-
current_step: analysis
|
|
9
|
-
assigned_to: ""
|
|
9
|
+
current_step: analysis # analysis | design | implementation | review | fix | commit
|
|
10
|
+
assigned_to: "" # claude | codex | gemini | opencode | human
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
# Task: [Title]
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
id: task-XXX
|
|
3
|
-
type: feature
|
|
3
|
+
type: feature # feature | bugfix | refactor | docs | review
|
|
4
|
+
branch: "" # <project>-<type>-<slug>
|
|
4
5
|
workflow: feature-development # feature-development | bug-fix | code-review | refactoring
|
|
5
|
-
status: open
|
|
6
|
+
status: open # open | in-progress | review | blocked | completed
|
|
6
7
|
created_at: YYYY-MM-DD
|
|
7
8
|
updated_at: YYYY-MM-DD
|
|
8
|
-
current_step: analysis
|
|
9
|
-
assigned_to: ""
|
|
9
|
+
current_step: analysis # analysis | design | implementation | review | fix | commit
|
|
10
|
+
assigned_to: "" # claude | codex | gemini | opencode | human
|
|
10
11
|
---
|
|
11
12
|
|
|
12
13
|
# 任务:[标题]
|