@fitlab-ai/agent-infra 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/README.md +12 -12
  2. package/README.zh-CN.md +12 -12
  3. package/bin/cli.ts +5 -1
  4. package/dist/bin/cli.js +6 -1
  5. package/dist/lib/defaults.json +4 -3
  6. package/dist/lib/sandbox/config.js +1 -1
  7. package/dist/package.json +1 -1
  8. package/lib/defaults.json +4 -3
  9. package/lib/sandbox/config.ts +1 -1
  10. package/package.json +3 -3
  11. package/templates/.agents/README.en.md +8 -8
  12. package/templates/.agents/README.zh-CN.md +8 -8
  13. package/templates/{.claude → .agents}/hooks/check-version-format.sh +3 -3
  14. package/templates/.agents/rules/create-issue.github.en.md +6 -0
  15. package/templates/.agents/rules/create-issue.github.zh-CN.md +6 -0
  16. package/templates/.agents/rules/issue-fields.github.en.md +155 -0
  17. package/templates/.agents/rules/issue-fields.github.zh-CN.md +155 -0
  18. package/templates/.agents/rules/issue-pr-commands.github.en.md +1 -0
  19. package/templates/.agents/rules/issue-pr-commands.github.zh-CN.md +1 -0
  20. package/templates/.agents/rules/issue-sync.github.en.md +2 -1
  21. package/templates/.agents/rules/issue-sync.github.zh-CN.md +2 -1
  22. package/templates/.agents/rules/task-management.en.md +17 -9
  23. package/templates/.agents/rules/task-management.zh-CN.md +17 -9
  24. package/templates/.agents/rules/testing-discipline.en.md +40 -0
  25. package/templates/.agents/rules/testing-discipline.zh-CN.md +40 -0
  26. package/templates/.agents/rules/version-stamp.en.md +29 -0
  27. package/templates/.agents/rules/version-stamp.zh-CN.md +29 -0
  28. package/templates/.agents/scripts/platform-adapters/platform-sync.github.js +143 -6
  29. package/templates/.agents/scripts/validate-artifact.js +32 -5
  30. package/templates/.agents/skills/analyze-task/SKILL.en.md +3 -0
  31. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +3 -0
  32. package/templates/.agents/skills/analyze-task/config/verify.json +2 -0
  33. package/templates/.agents/skills/block-task/SKILL.en.md +3 -0
  34. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +3 -0
  35. package/templates/.agents/skills/block-task/config/verify.json +1 -0
  36. package/templates/.agents/skills/cancel-task/SKILL.en.md +3 -0
  37. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +3 -0
  38. package/templates/.agents/skills/cancel-task/config/verify.json +1 -0
  39. package/templates/.agents/skills/commit/SKILL.en.md +10 -0
  40. package/templates/.agents/skills/commit/SKILL.zh-CN.md +10 -0
  41. package/templates/.agents/skills/commit/config/verify.json +1 -0
  42. package/templates/.agents/skills/commit/reference/task-status-update.en.md +5 -0
  43. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +5 -0
  44. package/templates/.agents/skills/complete-task/SKILL.en.md +4 -0
  45. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +4 -0
  46. package/templates/.agents/skills/complete-task/config/verify.json +2 -0
  47. package/templates/.agents/skills/create-pr/SKILL.en.md +5 -1
  48. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +5 -1
  49. package/templates/.agents/skills/create-pr/config/verify.json +1 -0
  50. package/templates/.agents/skills/create-task/SKILL.en.md +9 -0
  51. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +9 -0
  52. package/templates/.agents/skills/create-task/config/verify.json +1 -0
  53. package/templates/.agents/skills/implement-task/SKILL.en.md +16 -1
  54. package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +16 -1
  55. package/templates/.agents/skills/implement-task/config/verify.json +2 -0
  56. package/templates/.agents/skills/import-codescan/config/verify.json +1 -0
  57. package/templates/.agents/skills/import-dependabot/config/verify.json +1 -0
  58. package/templates/.agents/skills/import-issue/SKILL.en.md +10 -0
  59. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +10 -0
  60. package/templates/.agents/skills/import-issue/config/verify.json +1 -0
  61. package/templates/.agents/skills/plan-task/SKILL.en.md +3 -0
  62. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +3 -0
  63. package/templates/.agents/skills/plan-task/config/verify.json +2 -0
  64. package/templates/.agents/skills/refine-task/SKILL.en.md +15 -1
  65. package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +15 -1
  66. package/templates/.agents/skills/refine-task/config/verify.json +2 -0
  67. package/templates/.agents/skills/refine-task/reference/fix-workflow.en.md +9 -0
  68. package/templates/.agents/skills/refine-task/reference/fix-workflow.zh-CN.md +9 -0
  69. package/templates/.agents/skills/refine-task/reference/report-template.en.md +11 -0
  70. package/templates/.agents/skills/refine-task/reference/report-template.zh-CN.md +11 -0
  71. package/templates/.agents/skills/restore-task/SKILL.en.md +3 -0
  72. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +3 -0
  73. package/templates/.agents/skills/restore-task/config/verify.json +1 -0
  74. package/templates/.agents/skills/review-task/SKILL.en.md +16 -1
  75. package/templates/.agents/skills/review-task/SKILL.zh-CN.md +16 -1
  76. package/templates/.agents/skills/review-task/config/verify.json +3 -0
  77. package/templates/.agents/skills/review-task/reference/output-templates.en.md +20 -5
  78. package/templates/.agents/skills/review-task/reference/output-templates.zh-CN.md +20 -5
  79. package/templates/.agents/skills/review-task/reference/report-template.en.md +13 -0
  80. package/templates/.agents/skills/review-task/reference/report-template.zh-CN.md +13 -0
  81. package/templates/.agents/skills/review-task/reference/review-criteria.en.md +18 -0
  82. package/templates/.agents/skills/review-task/reference/review-criteria.zh-CN.md +18 -0
  83. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +4 -3
  84. package/templates/.agents/templates/task.en.md +5 -0
  85. package/templates/.agents/templates/task.zh-CN.md +5 -0
  86. package/templates/.claude/settings.json +1 -1
  87. package/templates/.codex/hooks.json +17 -0
package/README.md CHANGED
@@ -666,15 +666,15 @@ Use the top-level `.agents/.airc.json` `customTUIs` array when your team uses an
666
666
 
667
667
  | Field | Required | Meaning |
668
668
  |-------|----------|---------|
669
- | `name` | Yes | Display name shown in reports and next-step guidance, for example `Acme TUI`. |
670
- | `dir` | Yes | Command directory relative to the project root, for example `.acme/commands`. The path must stay inside the project root. |
669
+ | `name` | Yes | Display name shown in reports and next-step guidance, for example `<your-tui-name>`. |
670
+ | `dir` | Yes | Command directory relative to the project root, for example `.<your-tui>/commands`. The path must stay inside the project root. |
671
671
  | `invoke` | Yes | User-facing command template used in next-step guidance. |
672
672
 
673
673
  Supported `invoke` placeholders:
674
674
 
675
675
  | Placeholder | Replaced with | Example |
676
676
  |-------------|---------------|---------|
677
- | `${skillName}` | The skill command name, such as `review-task` or `commit`. | `acme ${skillName}` -> `acme review-task` |
677
+ | `${skillName}` | The skill command name, such as `review-task` or `commit`. | `<your-cli> ${skillName}` -> `<your-cli> review-task` |
678
678
  | `${projectName}` | The `.airc.json` `project` value. Use this for namespaced commands. | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
679
679
 
680
680
  Non-namespaced custom TUI:
@@ -683,9 +683,9 @@ Non-namespaced custom TUI:
683
683
  {
684
684
  "customTUIs": [
685
685
  {
686
- "name": "Acme TUI",
687
- "dir": ".acme/commands",
688
- "invoke": "acme ${skillName}"
686
+ "name": "<your-tui-name>",
687
+ "dir": ".<your-tui>/commands",
688
+ "invoke": "<your-cli> ${skillName}"
689
689
  }
690
690
  ]
691
691
  }
@@ -698,8 +698,8 @@ Namespaced custom TUI:
698
698
  "project": "agent-infra",
699
699
  "customTUIs": [
700
700
  {
701
- "name": "Internal Gemini",
702
- "dir": ".internal-gemini/commands",
701
+ "name": "<your-tui-name>",
702
+ "dir": ".<your-tui>/commands",
703
703
  "invoke": "/${projectName}:${skillName}"
704
704
  }
705
705
  ]
@@ -777,7 +777,7 @@ The generated `.agents/.airc.json` file is the central contract between the boot
777
777
  "project": "my-project",
778
778
  "org": "my-org",
779
779
  "language": "en",
780
- "templateVersion": "v0.6.0",
780
+ "templateVersion": "v0.6.1",
781
781
  "templates": {
782
782
  "sources": [
783
783
  { "type": "local", "path": "~/private-templates" }
@@ -790,9 +790,9 @@ The generated `.agents/.airc.json` file is the central contract between the boot
790
790
  },
791
791
  "customTUIs": [
792
792
  {
793
- "name": "Acme TUI",
794
- "dir": ".acme/commands",
795
- "invoke": "acme ${skillName}"
793
+ "name": "<your-tui-name>",
794
+ "dir": ".<your-tui>/commands",
795
+ "invoke": "<your-cli> ${skillName}"
796
796
  }
797
797
  ],
798
798
  "files": {
package/README.zh-CN.md CHANGED
@@ -642,15 +642,15 @@ args: "<task-id>" # 可选
642
642
 
643
643
  | 字段 | 必填 | 含义 |
644
644
  |------|------|------|
645
- | `name` | 是 | 报告和下一步提示中展示的工具名称,例如 `Acme TUI`。 |
646
- | `dir` | 是 | 相对项目根目录的命令目录,例如 `.acme/commands`。路径必须位于项目根目录内。 |
645
+ | `name` | 是 | 报告和下一步提示中展示的工具名称,例如 `<your-tui-name>`。 |
646
+ | `dir` | 是 | 相对项目根目录的命令目录,例如 `.<your-tui>/commands`。路径必须位于项目根目录内。 |
647
647
  | `invoke` | 是 | 面向用户展示的命令模板,用于生成下一步提示。 |
648
648
 
649
649
  `invoke` 支持的占位符:
650
650
 
651
651
  | 占位符 | 替换为 | 示例 |
652
652
  |--------|--------|------|
653
- | `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 | `acme ${skillName}` -> `acme review-task` |
653
+ | `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 | `<your-cli> ${skillName}` -> `<your-cli> review-task` |
654
654
  | `${projectName}` | `.airc.json` 中的 `project` 值,适用于带命名空间的命令。 | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
655
655
 
656
656
  不带命名空间的自定义 TUI:
@@ -659,9 +659,9 @@ args: "<task-id>" # 可选
659
659
  {
660
660
  "customTUIs": [
661
661
  {
662
- "name": "Acme TUI",
663
- "dir": ".acme/commands",
664
- "invoke": "acme ${skillName}"
662
+ "name": "<your-tui-name>",
663
+ "dir": ".<your-tui>/commands",
664
+ "invoke": "<your-cli> ${skillName}"
665
665
  }
666
666
  ]
667
667
  }
@@ -674,8 +674,8 @@ args: "<task-id>" # 可选
674
674
  "project": "agent-infra",
675
675
  "customTUIs": [
676
676
  {
677
- "name": "Internal Gemini",
678
- "dir": ".internal-gemini/commands",
677
+ "name": "<your-tui-name>",
678
+ "dir": ".<your-tui>/commands",
679
679
  "invoke": "/${projectName}:${skillName}"
680
680
  }
681
681
  ]
@@ -753,7 +753,7 @@ import-issue #42 从 GitHub Issue 导入任务
753
753
  "project": "my-project",
754
754
  "org": "my-org",
755
755
  "language": "en",
756
- "templateVersion": "v0.6.0",
756
+ "templateVersion": "v0.6.1",
757
757
  "templates": {
758
758
  "sources": [
759
759
  { "type": "local", "path": "~/private-templates" }
@@ -766,9 +766,9 @@ import-issue #42 从 GitHub Issue 导入任务
766
766
  },
767
767
  "customTUIs": [
768
768
  {
769
- "name": "Acme TUI",
770
- "dir": ".acme/commands",
771
- "invoke": "acme ${skillName}"
769
+ "name": "<your-tui-name>",
770
+ "dir": ".<your-tui>/commands",
771
+ "invoke": "<your-cli> ${skillName}"
772
772
  }
773
773
  ],
774
774
  "files": {
package/bin/cli.ts CHANGED
@@ -97,7 +97,11 @@ switch (command) {
97
97
  break;
98
98
  }
99
99
  case 'version': {
100
- console.log(`agent-infra ${VERSION}`);
100
+ if (process.argv[3] === '--raw') {
101
+ console.log(VERSION);
102
+ } else {
103
+ console.log(`agent-infra ${VERSION}`);
104
+ }
101
105
  break;
102
106
  }
103
107
  case 'help':
package/dist/bin/cli.js CHANGED
@@ -100,7 +100,12 @@ switch (command) {
100
100
  break;
101
101
  }
102
102
  case 'version': {
103
- console.log(`agent-infra ${VERSION}`);
103
+ if (process.argv[3] === '--raw') {
104
+ console.log(VERSION);
105
+ }
106
+ else {
107
+ console.log(`agent-infra ${VERSION}`);
108
+ }
104
109
  break;
105
110
  }
106
111
  case 'help':
@@ -10,8 +10,8 @@
10
10
  "tools": [
11
11
  "claude-code",
12
12
  "codex",
13
- "opencode",
14
- "gemini-cli"
13
+ "gemini-cli",
14
+ "opencode"
15
15
  ],
16
16
  "dockerfile": null,
17
17
  "vm": {
@@ -27,6 +27,7 @@
27
27
  "managed": [
28
28
  ".agents/QUICKSTART.md",
29
29
  ".agents/README.md",
30
+ ".agents/hooks/",
30
31
  ".agents/rules/",
31
32
  ".agents/scripts/",
32
33
  ".agents/skills/",
@@ -34,7 +35,7 @@
34
35
  ".agents/workflows/",
35
36
  ".agents/workspace/README.md",
36
37
  ".claude/commands/",
37
- ".claude/hooks/",
38
+ ".codex/hooks.json",
38
39
  ".gemini/commands/",
39
40
  ".git-hooks/check-version-format.sh",
40
41
  ".github/scripts/",
@@ -7,7 +7,7 @@ import { hostJoin } from "./engines/wsl2-paths.js";
7
7
  const DEFAULTS = Object.freeze({
8
8
  engine: null,
9
9
  runtimes: ['node20'],
10
- tools: ['claude-code', 'codex', 'opencode', 'gemini-cli'],
10
+ tools: ['claude-code', 'codex', 'gemini-cli', 'opencode'],
11
11
  dockerfile: null,
12
12
  vm: {
13
13
  cpu: null,
package/dist/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@fitlab-ai/agent-infra",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module"
5
5
  }
package/lib/defaults.json CHANGED
@@ -10,8 +10,8 @@
10
10
  "tools": [
11
11
  "claude-code",
12
12
  "codex",
13
- "opencode",
14
- "gemini-cli"
13
+ "gemini-cli",
14
+ "opencode"
15
15
  ],
16
16
  "dockerfile": null,
17
17
  "vm": {
@@ -27,6 +27,7 @@
27
27
  "managed": [
28
28
  ".agents/QUICKSTART.md",
29
29
  ".agents/README.md",
30
+ ".agents/hooks/",
30
31
  ".agents/rules/",
31
32
  ".agents/scripts/",
32
33
  ".agents/skills/",
@@ -34,7 +35,7 @@
34
35
  ".agents/workflows/",
35
36
  ".agents/workspace/README.md",
36
37
  ".claude/commands/",
37
- ".claude/hooks/",
38
+ ".codex/hooks.json",
38
39
  ".gemini/commands/",
39
40
  ".git-hooks/check-version-format.sh",
40
41
  ".github/scripts/",
@@ -8,7 +8,7 @@ import { hostJoin } from './engines/wsl2-paths.ts';
8
8
  const DEFAULTS = Object.freeze({
9
9
  engine: null,
10
10
  runtimes: ['node20'],
11
- tools: ['claude-code', 'codex', 'opencode', 'gemini-cli'],
11
+ tools: ['claude-code', 'codex', 'gemini-cli', 'opencode'],
12
12
  dockerfile: null,
13
13
  vm: {
14
14
  cpu: null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fitlab-ai/agent-infra",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Bootstrap tool for AI multi-tool collaboration infrastructure — works with Claude Code, Codex, Gemini CLI, and OpenCode",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -60,7 +60,7 @@
60
60
  },
61
61
  "devDependencies": {
62
62
  "@types/cross-spawn": "^6.0.6",
63
- "@types/node": "^22.19.19",
64
- "typescript": "~5.9"
63
+ "@types/node": "^25.9.1",
64
+ "typescript": "~6.0"
65
65
  }
66
66
  }
@@ -199,15 +199,15 @@ Use the top-level `.agents/.airc.json` `customTUIs` array when your team uses an
199
199
 
200
200
  | Field | Required | Meaning |
201
201
  |-------|----------|---------|
202
- | `name` | Yes | Display name shown in reports and next-step guidance, for example `Acme TUI`. |
203
- | `dir` | Yes | Command directory relative to the project root, for example `.acme/commands`. The path must stay inside the project root. |
202
+ | `name` | Yes | Display name shown in reports and next-step guidance, for example `<your-tui-name>`. |
203
+ | `dir` | Yes | Command directory relative to the project root, for example `.<your-tui>/commands`. The path must stay inside the project root. |
204
204
  | `invoke` | Yes | User-facing command template used in next-step guidance. |
205
205
 
206
206
  Supported `invoke` placeholders:
207
207
 
208
208
  | Placeholder | Replaced with | Example |
209
209
  |-------------|---------------|---------|
210
- | `${skillName}` | The skill command name, such as `review-task` or `commit`. | `acme ${skillName}` -> `acme review-task` |
210
+ | `${skillName}` | The skill command name, such as `review-task` or `commit`. | `<your-cli> ${skillName}` -> `<your-cli> review-task` |
211
211
  | `${projectName}` | The `.airc.json` `project` value. Use this for namespaced commands. | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
212
212
 
213
213
  Non-namespaced custom TUI:
@@ -216,9 +216,9 @@ Non-namespaced custom TUI:
216
216
  {
217
217
  "customTUIs": [
218
218
  {
219
- "name": "Acme TUI",
220
- "dir": ".acme/commands",
221
- "invoke": "acme ${skillName}"
219
+ "name": "<your-tui-name>",
220
+ "dir": ".<your-tui>/commands",
221
+ "invoke": "<your-cli> ${skillName}"
222
222
  }
223
223
  ]
224
224
  }
@@ -231,8 +231,8 @@ Namespaced custom TUI:
231
231
  "project": "agent-infra",
232
232
  "customTUIs": [
233
233
  {
234
- "name": "Internal Gemini",
235
- "dir": ".internal-gemini/commands",
234
+ "name": "<your-tui-name>",
235
+ "dir": ".<your-tui>/commands",
236
236
  "invoke": "/${projectName}:${skillName}"
237
237
  }
238
238
  ]
@@ -199,15 +199,15 @@ args: "<task-id>" # 可选
199
199
 
200
200
  | 字段 | 必填 | 含义 |
201
201
  |------|------|------|
202
- | `name` | 是 | 报告和下一步提示中展示的工具名称,例如 `Acme TUI`。 |
203
- | `dir` | 是 | 相对项目根目录的命令目录,例如 `.acme/commands`。路径必须位于项目根目录内。 |
202
+ | `name` | 是 | 报告和下一步提示中展示的工具名称,例如 `<your-tui-name>`。 |
203
+ | `dir` | 是 | 相对项目根目录的命令目录,例如 `.<your-tui>/commands`。路径必须位于项目根目录内。 |
204
204
  | `invoke` | 是 | 面向用户展示的命令模板,用于生成下一步提示。 |
205
205
 
206
206
  `invoke` 支持的占位符:
207
207
 
208
208
  | 占位符 | 替换为 | 示例 |
209
209
  |--------|--------|------|
210
- | `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 | `acme ${skillName}` -> `acme review-task` |
210
+ | `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 | `<your-cli> ${skillName}` -> `<your-cli> review-task` |
211
211
  | `${projectName}` | `.airc.json` 中的 `project` 值,适用于带命名空间的命令。 | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
212
212
 
213
213
  不带命名空间的自定义 TUI:
@@ -216,9 +216,9 @@ args: "<task-id>" # 可选
216
216
  {
217
217
  "customTUIs": [
218
218
  {
219
- "name": "Acme TUI",
220
- "dir": ".acme/commands",
221
- "invoke": "acme ${skillName}"
219
+ "name": "<your-tui-name>",
220
+ "dir": ".<your-tui>/commands",
221
+ "invoke": "<your-cli> ${skillName}"
222
222
  }
223
223
  ]
224
224
  }
@@ -231,8 +231,8 @@ args: "<task-id>" # 可选
231
231
  "project": "agent-infra",
232
232
  "customTUIs": [
233
233
  {
234
- "name": "Internal Gemini",
235
- "dir": ".internal-gemini/commands",
234
+ "name": "<your-tui-name>",
235
+ "dir": ".<your-tui>/commands",
236
236
  "invoke": "/${projectName}:${skillName}"
237
237
  }
238
238
  ]
@@ -10,7 +10,7 @@ hook_command=$(
10
10
  try {
11
11
  const payload = JSON.parse(Buffer.concat(chunks).toString());
12
12
  process.stdout.write(payload.tool_input && payload.tool_input.command || "");
13
- } catch (error) {
13
+ } catch {
14
14
  process.stdout.write("");
15
15
  }
16
16
  });
@@ -30,14 +30,14 @@ repo_root=$(
30
30
  )
31
31
 
32
32
  if sh "$repo_root/.git-hooks/check-version-format.sh"; then
33
- echo "Claude hook: version check passed."
33
+ echo "AI hook: version check passed."
34
34
  exit 0
35
35
  else
36
36
  status=$?
37
37
  fi
38
38
 
39
39
  if [ "$status" -eq 1 ]; then
40
- echo "Claude hook: blocking git commit (version format error)."
40
+ echo "AI hook: blocking git commit (version format error)." >&2
41
41
  exit 2
42
42
  fi
43
43
 
@@ -149,6 +149,12 @@ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH \
149
149
 
150
150
  Failure is non-blocking.
151
151
 
152
+ ### 6.5 Set Issue Fields (Optional)
153
+
154
+ If `has_push=true`, read `.agents/rules/issue-fields.md` and follow Flow A to write any applicable non-empty `priority`, `effort`, `start_date`, and `target_date` values from `task.md`.
155
+
156
+ Field write failures are non-blocking.
157
+
152
158
  ### 7. Write Back task.md
153
159
 
154
160
  Update task.md:
@@ -149,6 +149,12 @@ gh api "repos/$upstream_repo/issues/{issue-number}" -X PATCH \
149
149
 
150
150
  设置失败不阻断流程。
151
151
 
152
+ ### 6.5 设置 Issue 字段(可选)
153
+
154
+ 如果 `has_push=true`,读取 `.agents/rules/issue-fields.md`,按流程 A 写入 `task.md` 中适用且非空的 `priority`、`effort`、`start_date` 和 `target_date`。
155
+
156
+ 字段写入失败不阻断流程。
157
+
152
158
  ### 7. 回写 task.md
153
159
 
154
160
  更新 task.md:
@@ -0,0 +1,155 @@
1
+ # Issue Fields
2
+
3
+ Read this file before writing or verifying Issue Type pinned custom fields.
4
+
5
+ ## Boundary
6
+
7
+ - Use this rule only after `upstream_repo`, `has_push`, and the Issue number are known.
8
+ - If `has_push=false`, skip direct field writes and continue.
9
+ - Fetch the organization's current Issue Type schema before each write; do not hard-code the field set.
10
+ - Missing, empty, or unresolvable values are skipped. Field writes are best-effort and must not block the workflow.
11
+
12
+ ## Supported Task Frontmatter
13
+
14
+ All fields are optional:
15
+
16
+ | task.md field | Issue field | Value format |
17
+ |---|---|---|
18
+ | `priority` | `Priority` | `Urgent`, `High`, `Medium`, or `Low` |
19
+ | `effort` | `Effort` | `High`, `Medium`, or `Low` |
20
+ | `start_date` | `Start date` | `YYYY-MM-DD` |
21
+ | `target_date` | `Target date` | `YYYY-MM-DD` |
22
+
23
+ Localized option input may be normalized before writing:
24
+
25
+ | Input | Stored option |
26
+ |---|---|
27
+ | `紧急` | `Urgent` |
28
+ | `高` | `High` |
29
+ | `中` | `Medium` |
30
+ | `低` | `Low` |
31
+
32
+ AI agents may infer `priority` and `effort` from the title and description when creating or refining tasks, but must keep date fields empty unless the user or source Issue provides explicit dates. Human edits in `task.md` take precedence.
33
+
34
+ ## GraphQL Reference
35
+
36
+ Read Issue Type pinned fields:
37
+
38
+ ```graphql
39
+ query($owner:String!){
40
+ organization(login:$owner){ issueTypes(first:20){ nodes{
41
+ id name
42
+ pinnedFields{
43
+ __typename
44
+ ... on IssueFieldSingleSelect{ id name options{ id name } }
45
+ ... on IssueFieldDate{ id name }
46
+ ... on IssueFieldText{ id name }
47
+ ... on IssueFieldNumber{ id name }
48
+ }
49
+ } } }
50
+ }
51
+ ```
52
+
53
+ Read one Issue's current type and field values:
54
+
55
+ ```graphql
56
+ query($owner:String!,$name:String!,$number:Int!){
57
+ repository(owner:$owner,name:$name){ issue(number:$number){
58
+ id
59
+ issueType{ name pinnedFields{
60
+ __typename
61
+ ... on IssueFieldSingleSelect{ id name options{ id name } }
62
+ ... on IssueFieldDate{ id name }
63
+ ... on IssueFieldText{ id name }
64
+ ... on IssueFieldNumber{ id name }
65
+ } }
66
+ issueFieldValues(first:50){ nodes{
67
+ __typename
68
+ ... on IssueFieldSingleSelectValue{ name optionId field{ ... on IssueFieldSingleSelect{ name } } }
69
+ ... on IssueFieldDateValue{ value field{ ... on IssueFieldDate{ name } } }
70
+ ... on IssueFieldTextValue{ value field{ ... on IssueFieldText{ name } } }
71
+ ... on IssueFieldNumberValue{ value field{ ... on IssueFieldNumber{ name } } }
72
+ } }
73
+ } }
74
+ }
75
+ ```
76
+
77
+ Write or clear fields and update Issue Type:
78
+
79
+ ```graphql
80
+ mutation($issueId:ID!,$issueFields:[IssueFieldCreateOrUpdateInput!]!){
81
+ setIssueFieldValue(input:{issueId:$issueId,issueFields:$issueFields}){ issue{ id } }
82
+ }
83
+
84
+ mutation($issueId:ID!,$issueTypeId:ID){
85
+ updateIssueIssueType(input:{issueId:$issueId,issueTypeId:$issueTypeId}){ issue{ id } }
86
+ }
87
+ ```
88
+
89
+ `IssueFieldCreateOrUpdateInput` supports `fieldId`, `singleSelectOptionId`, `dateValue`, `textValue`, `numberValue`, and `delete`.
90
+
91
+ Minimal command shells:
92
+
93
+ ```bash
94
+ gh api graphql \
95
+ -f query='query($owner:String!){organization(login:$owner){issueTypes(first:20){nodes{id name pinnedFields{__typename ... on IssueFieldSingleSelect{id name options{id name}} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}}}}}' \
96
+ -F owner="{owner}"
97
+
98
+ gh api graphql \
99
+ -f query='query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){issue(number:$number){id issueType{name pinnedFields{__typename ... on IssueFieldSingleSelect{id name options{id name}} ... on IssueFieldDate{id name} ... on IssueFieldText{id name} ... on IssueFieldNumber{id name}}} issueFieldValues(first:50){nodes{__typename ... on IssueFieldSingleSelectValue{name optionId field{... on IssueFieldSingleSelect{name}}} ... on IssueFieldDateValue{value field{... on IssueFieldDate{name}}} ... on IssueFieldTextValue{value field{... on IssueFieldText{name}}} ... on IssueFieldNumberValue{value field{... on IssueFieldNumber{name}}}}}}}}' \
100
+ -F owner="{owner}" -F name="{repo}" -F number="{issue-number}"
101
+
102
+ gh api graphql --input - <<'JSON'
103
+ {
104
+ "query": "mutation($issueId:ID!,$issueFields:[IssueFieldCreateOrUpdateInput!]!){setIssueFieldValue(input:{issueId:$issueId,issueFields:$issueFields}){issue{id}}}",
105
+ "variables": {
106
+ "issueId": "{issue-id}",
107
+ "issueFields": [
108
+ { "fieldId": "{field-id}", "singleSelectOptionId": "{option-id}" },
109
+ { "fieldId": "{date-field-id}", "dateValue": "YYYY-MM-DD" },
110
+ { "fieldId": "{old-field-id}", "delete": true }
111
+ ]
112
+ }
113
+ }
114
+ JSON
115
+
116
+ gh api graphql --input - <<'JSON'
117
+ {
118
+ "query": "mutation($issueId:ID!,$issueTypeId:ID){updateIssueIssueType(input:{issueId:$issueId,issueTypeId:$issueTypeId}){issue{id}}}",
119
+ "variables": {
120
+ "issueId": "{issue-id}",
121
+ "issueTypeId": "{issue-type-id}"
122
+ }
123
+ }
124
+ JSON
125
+ ```
126
+
127
+ Values not listed in the localization table are treated as literal option names, which is intended for canonical English input.
128
+
129
+ ## Flow A: Write Fields After Issue Creation
130
+
131
+ 1. Stop if `has_push` is not `true`.
132
+ 2. Resolve `{owner}` from `$upstream_repo` and query `organization.issueTypes`.
133
+ 3. Select the target Issue Type's `pinnedFields`.
134
+ 4. Read non-empty `priority`, `effort`, `start_date`, and `target_date` values from `task.md`.
135
+ 5. For each value:
136
+ - Skip it when the target type does not pin a same-name field.
137
+ - For single-select fields, normalize localized input and match the option by name.
138
+ - For date fields, write only `YYYY-MM-DD` values.
139
+ 6. Submit one `setIssueFieldValue` mutation with all resolved inputs. If no input remains, skip.
140
+
141
+ ## Flow B: Set Type And Migrate Fields
142
+
143
+ Use this flow whenever an existing Issue Type is changed.
144
+
145
+ 1. Stop if `has_push` is not `true`.
146
+ 2. Read the Issue id, current Issue Type, pinned fields, and current field values.
147
+ 3. Query the organization Issue Type list and resolve the target Issue Type id.
148
+ 4. Run `updateIssueIssueType` with the target Issue Type id.
149
+ 5. Resolve the target type's pinned fields.
150
+ 6. For each old field value:
151
+ - If the target type has a same-name field, write the value again. For single-select values, resolve the target option id by option name.
152
+ - If the target type does not have a same-name field, send `{ fieldId, delete: true }` for the old field.
153
+ 7. Submit one `setIssueFieldValue` mutation with all migration inputs. Empty migrations are skipped.
154
+
155
+ Both flows are idempotent. Rewriting an unchanged value or deleting an already empty field is acceptable.