@fitlab-ai/agent-infra 0.5.6 → 0.5.7

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 (100) hide show
  1. package/README.md +92 -4
  2. package/README.zh-CN.md +92 -4
  3. package/bin/cli.js +28 -4
  4. package/lib/defaults.json +1 -0
  5. package/lib/init.js +68 -4
  6. package/lib/prompt.js +28 -1
  7. package/lib/render.js +1 -1
  8. package/lib/sandbox/commands/rm.js +6 -4
  9. package/lib/sandbox/commands/vm.js +43 -16
  10. package/lib/sandbox/config.js +5 -0
  11. package/lib/sandbox/engine.js +125 -16
  12. package/lib/sandbox/task-resolver.js +13 -6
  13. package/package.json +2 -2
  14. package/templates/.agents/QUICKSTART.en.md +17 -0
  15. package/templates/.agents/QUICKSTART.zh-CN.md +17 -0
  16. package/templates/.agents/README.en.md +70 -1
  17. package/templates/.agents/README.zh-CN.md +70 -1
  18. package/templates/.agents/rules/issue-pr-commands.en.md +5 -0
  19. package/templates/.agents/rules/issue-pr-commands.zh-CN.md +5 -0
  20. package/templates/.agents/rules/issue-sync.en.md +5 -0
  21. package/templates/.agents/rules/issue-sync.zh-CN.md +5 -0
  22. package/templates/.agents/rules/label-milestone-setup.en.md +5 -0
  23. package/templates/.agents/rules/label-milestone-setup.zh-CN.md +5 -0
  24. package/templates/.agents/rules/milestone-inference.en.md +5 -0
  25. package/templates/.agents/rules/milestone-inference.zh-CN.md +5 -0
  26. package/templates/.agents/rules/pr-sync.en.md +5 -0
  27. package/templates/.agents/rules/pr-sync.zh-CN.md +5 -0
  28. package/templates/.agents/rules/release-commands.en.md +5 -0
  29. package/templates/.agents/rules/release-commands.zh-CN.md +5 -0
  30. package/templates/.agents/rules/security-alerts.en.md +5 -0
  31. package/templates/.agents/rules/security-alerts.zh-CN.md +5 -0
  32. package/templates/.agents/scripts/platform-adapters/platform-sync.js +6 -0
  33. package/templates/.agents/skills/analyze-task/SKILL.en.md +2 -2
  34. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +2 -2
  35. package/templates/.agents/skills/block-task/SKILL.en.md +1 -1
  36. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +1 -1
  37. package/templates/.agents/skills/cancel-task/SKILL.en.md +1 -1
  38. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +2 -2
  39. package/templates/.agents/skills/check-task/SKILL.en.md +1 -1
  40. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +1 -1
  41. package/templates/.agents/skills/close-codescan/SKILL.en.md +1 -1
  42. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +1 -1
  43. package/templates/.agents/skills/close-dependabot/SKILL.en.md +1 -1
  44. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +1 -1
  45. package/templates/.agents/skills/commit/SKILL.en.md +1 -1
  46. package/templates/.agents/skills/commit/SKILL.zh-CN.md +1 -1
  47. package/templates/.agents/skills/create-issue/SKILL.en.md +2 -2
  48. package/templates/.agents/skills/create-issue/SKILL.zh-CN.md +2 -2
  49. package/templates/.agents/skills/create-pr/SKILL.en.md +1 -1
  50. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +1 -1
  51. package/templates/.agents/skills/create-release-note/SKILL.en.md +8 -1
  52. package/templates/.agents/skills/create-release-note/SKILL.zh-CN.md +8 -1
  53. package/templates/.agents/skills/create-task/SKILL.en.md +2 -2
  54. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +2 -2
  55. package/templates/.agents/skills/implement-task/SKILL.en.md +2 -2
  56. package/templates/.agents/skills/implement-task/SKILL.zh-CN.md +2 -2
  57. package/templates/.agents/skills/import-codescan/SKILL.en.md +2 -2
  58. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +2 -2
  59. package/templates/.agents/skills/import-dependabot/SKILL.en.md +2 -2
  60. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +2 -2
  61. package/templates/.agents/skills/import-issue/SKILL.en.md +2 -2
  62. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +2 -2
  63. package/templates/.agents/skills/init-labels/SKILL.en.md +1 -1
  64. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +1 -1
  65. package/templates/.agents/skills/init-labels/scripts/init-labels.sh +6 -0
  66. package/templates/.agents/skills/init-milestones/SKILL.en.md +1 -1
  67. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +1 -1
  68. package/templates/.agents/skills/init-milestones/scripts/init-milestones.sh +6 -0
  69. package/templates/.agents/skills/plan-task/SKILL.en.md +2 -2
  70. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +2 -2
  71. package/templates/.agents/skills/post-release/SKILL.en.md +95 -0
  72. package/templates/.agents/skills/post-release/SKILL.zh-CN.md +95 -0
  73. package/templates/.agents/skills/refine-task/SKILL.en.md +1 -1
  74. package/templates/.agents/skills/refine-task/SKILL.zh-CN.md +1 -1
  75. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  76. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  77. package/templates/.agents/skills/release/SKILL.en.md +6 -1
  78. package/templates/.agents/skills/release/SKILL.zh-CN.md +6 -1
  79. package/templates/.agents/skills/release/scripts/manage-milestones.sh +6 -0
  80. package/templates/.agents/skills/restore-task/SKILL.en.md +2 -2
  81. package/templates/.agents/skills/restore-task/SKILL.zh-CN.md +2 -2
  82. package/templates/.agents/skills/review-task/SKILL.en.md +2 -2
  83. package/templates/.agents/skills/review-task/SKILL.zh-CN.md +2 -2
  84. package/templates/.agents/skills/test/SKILL.en.md +1 -1
  85. package/templates/.agents/skills/test/SKILL.zh-CN.md +1 -1
  86. package/templates/.agents/skills/test-integration/SKILL.en.md +1 -1
  87. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +1 -1
  88. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +10 -2
  89. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +4 -2
  90. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +289 -12
  91. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +1 -1
  92. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +1 -1
  93. package/templates/.agents/templates/task.en.md +2 -2
  94. package/templates/.agents/templates/task.zh-CN.md +2 -2
  95. package/templates/.claude/commands/post-release.en.md +8 -0
  96. package/templates/.claude/commands/post-release.zh-CN.md +8 -0
  97. package/templates/.gemini/commands/_project_/post-release.en.toml +6 -0
  98. package/templates/.gemini/commands/_project_/post-release.zh-CN.toml +6 -0
  99. package/templates/.opencode/commands/post-release.en.md +9 -0
  100. package/templates/.opencode/commands/post-release.zh-CN.md +9 -0
@@ -2,26 +2,56 @@ import { platform } from 'node:os';
2
2
  import { detectHostResources } from './constants.js';
3
3
  import { run, runOk, runSafe, runVerbose } from './shell.js';
4
4
 
5
- export function detectEngine() {
6
- const os = platform();
7
- if (os === 'darwin') {
8
- return 'colima';
5
+ export const ENGINES = Object.freeze({
6
+ COLIMA: 'colima',
7
+ ORBSTACK: 'orbstack',
8
+ DOCKER_DESKTOP: 'docker-desktop'
9
+ });
10
+
11
+ const VALID_CONFIG_ENGINES = new Set(Object.values(ENGINES));
12
+
13
+ export function validateSandboxEngine(engine) {
14
+ if (engine === null || engine === undefined) {
15
+ return null;
9
16
  }
17
+
18
+ if (VALID_CONFIG_ENGINES.has(engine)) {
19
+ return engine;
20
+ }
21
+
22
+ throw new Error(
23
+ `sandbox: invalid "sandbox.engine" value "${engine}". `
24
+ + 'Expected one of: null, colima, orbstack, docker-desktop. '
25
+ + 'This setting only affects macOS sandbox engine selection.'
26
+ );
27
+ }
28
+
29
+ export function detectEngine(config = {}, { platformFn = platform } = {}) {
30
+ const configured = validateSandboxEngine(config.engine);
31
+ const os = platformFn();
10
32
  if (os === 'linux') {
11
33
  return 'native';
12
34
  }
13
35
  if (os === 'win32') {
14
36
  return 'wsl2';
15
37
  }
38
+ if (os === 'darwin') {
39
+ if (configured) {
40
+ return configured;
41
+ }
42
+
43
+ return ENGINES.COLIMA;
44
+ }
16
45
  return 'unsupported';
17
46
  }
18
47
 
19
48
  function colimaArgs(config, runSafeFn = runSafe) {
20
49
  const arch = runSafeFn('uname', ['-m']);
21
50
  const defaults = detectHostResources();
22
- const cpu = config.vm.cpu ?? defaults.cpu;
23
- const memory = config.vm.memory ?? defaults.memory;
24
- const disk = config.vm.disk ?? 60;
51
+ const vm = config.vm ?? {};
52
+ const cpu = vm.cpu ?? defaults.cpu;
53
+ const memory = vm.memory ?? defaults.memory;
54
+ const disk = vm.disk ?? 60;
25
55
  const args = ['start', '--cpu', String(cpu), '--memory', String(memory), '--disk', String(disk)];
26
56
 
27
57
  if (arch === 'arm64') {
@@ -53,14 +83,54 @@ export async function ensureColima(
53
83
  }
54
84
  }
55
85
 
86
+ export async function ensureOrbStack(
87
+ _config,
88
+ onMessage,
89
+ { runOkFn = runOk, runVerboseFn = runVerbose } = {}
90
+ ) {
91
+ if (!runOkFn('which', ['orb'])) {
92
+ onMessage?.('Installing OrbStack via Homebrew...');
93
+ runVerboseFn('brew', ['install', '--cask', 'orbstack']);
94
+ }
95
+
96
+ if (!runOkFn('docker', ['info'])) {
97
+ onMessage?.('Starting OrbStack...');
98
+ runVerboseFn('orb', ['start']);
99
+ }
100
+
101
+ if (!runOkFn('docker', ['info'])) {
102
+ throw new Error('Docker daemon is not available after starting OrbStack');
103
+ }
104
+ }
105
+
106
+ export async function ensureDockerDesktop(
107
+ _config,
108
+ onMessage,
109
+ { runOkFn = runOk } = {}
110
+ ) {
111
+ if (!runOkFn('docker', ['info'])) {
112
+ throw new Error('Docker Desktop is not running. Please start Docker Desktop manually.');
113
+ }
114
+ }
115
+
56
116
  export async function ensureDocker(config, onMessage) {
57
- const engine = detectEngine();
117
+ const engine = detectEngine(config);
58
118
 
59
- if (engine === 'colima') {
119
+ if (engine === ENGINES.COLIMA) {
60
120
  await ensureColima(config, onMessage);
61
121
  return;
62
122
  }
63
123
 
124
+ if (engine === ENGINES.ORBSTACK) {
125
+ await ensureOrbStack(config, onMessage);
126
+ return;
127
+ }
128
+
129
+ if (engine === ENGINES.DOCKER_DESKTOP) {
130
+ await ensureDockerDesktop(config, onMessage);
131
+ return;
132
+ }
133
+
64
134
  if (engine === 'native') {
65
135
  if (!runOk('docker', ['info'])) {
66
136
  throw new Error('Docker daemon is not running. Please start Docker first.');
@@ -75,19 +145,58 @@ export async function ensureDocker(config, onMessage) {
75
145
  throw new Error(`Unsupported sandbox engine: ${engine}`);
76
146
  }
77
147
 
78
- export function isVmManaged() {
79
- return detectEngine() === 'colima';
148
+ export function isVmManaged(config = {}, dependencies = {}) {
149
+ const engine = detectEngine(config, dependencies);
150
+ return isManagedEngine(engine);
80
151
  }
81
152
 
82
- export function startManagedVm(config) {
83
- if (!isVmManaged()) {
84
- throw new Error('VM management is only available on macOS with Colima.');
153
+ export function isManagedEngine(engine) {
154
+ return engine === ENGINES.COLIMA || engine === ENGINES.ORBSTACK;
155
+ }
156
+
157
+ export function engineDisplayName(engine) {
158
+ const names = {
159
+ [ENGINES.COLIMA]: 'Colima',
160
+ [ENGINES.ORBSTACK]: 'OrbStack',
161
+ [ENGINES.DOCKER_DESKTOP]: 'Docker Desktop',
162
+ native: 'native Docker',
163
+ wsl2: 'WSL2'
164
+ };
165
+ return names[engine] ?? engine;
166
+ }
167
+
168
+ export function startManagedVm(
169
+ config,
170
+ { platformFn = platform, runOkFn = runOk, runVerboseFn = runVerbose } = {}
171
+ ) {
172
+ const engine = detectEngine(config, { platformFn });
173
+ if (!isManagedEngine(engine)) {
174
+ throw new Error(`VM management is unavailable for engine '${engineDisplayName(engine)}'.`);
85
175
  }
86
176
 
87
- if (runOk('colima', ['status'])) {
177
+ if (engine === ENGINES.COLIMA && runOkFn('colima', ['status'])) {
178
+ return 'already-running';
179
+ }
180
+ if (engine === ENGINES.ORBSTACK && runOkFn('orb', ['status'])) {
88
181
  return 'already-running';
89
182
  }
90
183
 
91
- runVerbose('colima', colimaArgs(config));
184
+ if (engine === ENGINES.COLIMA) {
185
+ runVerboseFn('colima', colimaArgs(config));
186
+ } else if (engine === ENGINES.ORBSTACK) {
187
+ runVerboseFn('orb', ['start']);
188
+ }
92
189
  return 'started';
93
190
  }
191
+
192
+ export function stopManagedVm(config, { platformFn = platform, runFn = run } = {}) {
193
+ const engine = detectEngine(config, { platformFn });
194
+ if (engine === ENGINES.COLIMA) {
195
+ runFn('colima', ['stop']);
196
+ return 'stopped';
197
+ } else if (engine === ENGINES.ORBSTACK) {
198
+ runFn('orb', ['stop']);
199
+ return 'stopped';
200
+ }
201
+ throw new Error(`VM management is unavailable for engine '${engineDisplayName(engine)}'.`);
202
+ }
@@ -2,24 +2,31 @@ import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
4
  const TASK_ID_RE = /^TASK-\d{8}-\d{6}$/;
5
+ const WORKSPACE_DIRS = ['active', 'completed', 'blocked', 'archive'];
6
+
7
+ function stripQuotes(value) {
8
+ return value.replace(/^(["'])(.*)\1$/, '$2');
9
+ }
5
10
 
6
11
  function readTaskContent(repoRoot, taskId) {
7
- const taskPath = path.join(repoRoot, '.agents', 'workspace', 'active', taskId, 'task.md');
8
- if (!fs.existsSync(taskPath)) {
9
- throw new Error(`Task not found: ${taskId}`);
12
+ for (const dir of WORKSPACE_DIRS) {
13
+ const taskPath = path.join(repoRoot, '.agents', 'workspace', dir, taskId, 'task.md');
14
+ if (fs.existsSync(taskPath)) {
15
+ return fs.readFileSync(taskPath, 'utf8');
16
+ }
10
17
  }
11
- return fs.readFileSync(taskPath, 'utf8');
18
+ throw new Error(`Task not found: ${taskId}`);
12
19
  }
13
20
 
14
21
  function resolveBranchFromTaskContent(content, taskId) {
15
22
  const frontmatterBranch = content.match(/^branch:\s*(.+)$/m);
16
23
  if (frontmatterBranch && frontmatterBranch[1].trim()) {
17
- return frontmatterBranch[1].trim();
24
+ return stripQuotes(frontmatterBranch[1].trim());
18
25
  }
19
26
 
20
27
  const contextBranch = content.match(/^- \*\*(?:分支|Branch)\*\*:[ \t]*`?([^`\n]+)`?$/m);
21
28
  if (contextBranch && contextBranch[1].trim()) {
22
- return contextBranch[1].trim();
29
+ return stripQuotes(contextBranch[1].trim());
23
30
  }
24
31
 
25
32
  throw new Error(`Task ${taskId} has no branch field in task.md`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fitlab-ai/agent-infra",
3
- "version": "0.5.6",
3
+ "version": "0.5.7",
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",
@@ -46,7 +46,7 @@
46
46
  },
47
47
  "scripts": {
48
48
  "build": "node scripts/build-inline.js",
49
- "demo:regen": "vhs assets/demo-init.tape",
49
+ "demo:regen": "sh scripts/demo-regen.sh",
50
50
  "prepare": "git config core.hooksPath .github/hooks || true",
51
51
  "test": "node scripts/build-inline.js --check && node --test tests/cli/*.test.js tests/templates/*.test.js tests/core/*.test.js",
52
52
  "prepublishOnly": "node scripts/build-inline.js --check && node --test tests/cli/*.test.js tests/templates/*.test.js tests/core/*.test.js"
@@ -18,6 +18,23 @@ git config core.hooksPath .github/hooks
18
18
 
19
19
  This makes Git invoke the hooks synced into `.github/hooks/`, including `pre-commit` and `check-version-format.sh`.
20
20
 
21
+ ## External Templates And Skills
22
+
23
+ If your team maintains private platform templates or shared custom skills, configure local sources in `.agents/.airc.json`:
24
+
25
+ ```json
26
+ {
27
+ "templates": {
28
+ "sources": [{ "type": "local", "path": "~/private-templates" }]
29
+ },
30
+ "skills": {
31
+ "sources": [{ "type": "local", "path": "~/private-skills" }]
32
+ }
33
+ }
34
+ ```
35
+
36
+ Built-in templates take priority over external templates. Later external template sources override earlier external sources, and `update-agent-infra` reports conflicts in `templateSources.conflicts`. Only use trusted local paths because external templates and skills may contain executable scripts.
37
+
21
38
  ## Creating Your First Task
22
39
 
23
40
  1. Copy the task template to the active workspace:
@@ -18,6 +18,23 @@ git config core.hooksPath .github/hooks
18
18
 
19
19
  这样 Git 才会调用同步到 `.github/hooks/` 下的 hook,包括 `pre-commit` 和 `check-version-format.sh`。
20
20
 
21
+ ## 外部模板与 Skill
22
+
23
+ 如果团队维护私有平台模板或共享自定义 skill,可在 `.agents/.airc.json` 中配置本地源:
24
+
25
+ ```json
26
+ {
27
+ "templates": {
28
+ "sources": [{ "type": "local", "path": "~/private-templates" }]
29
+ },
30
+ "skills": {
31
+ "sources": [{ "type": "local", "path": "~/private-skills" }]
32
+ }
33
+ }
34
+ ```
35
+
36
+ 内置模板优先级高于外部模板。多个外部模板源之间,后面的 source 覆盖前面的 source,`update-agent-infra` 会在 `templateSources.conflicts` 中报告冲突。外部模板和 skill 可能包含可执行脚本,只使用可信本地路径。
37
+
21
38
  ## 创建第一个任务
22
39
 
23
40
  1. 将任务模板复制到活跃工作区:
@@ -120,6 +120,27 @@ To adapt agent-infra to a private code-hosting platform:
120
120
  4. If you maintain a fork of the template source, add matching `.{platform}.` template variants before adding that platform identifier to the sync logic.
121
121
  5. Validate the customized workflow on a test task before rolling it out broadly.
122
122
 
123
+ ## External Template And Skill Sources
124
+
125
+ Teams can configure external template sources and shared skill sources in `.agents/.airc.json` for private platform templates, private rules, and shared custom skills:
126
+
127
+ ```json
128
+ {
129
+ "templates": {
130
+ "sources": [
131
+ { "type": "local", "path": "~/private-templates" }
132
+ ]
133
+ },
134
+ "skills": {
135
+ "sources": [
136
+ { "type": "local", "path": "~/private-skills" }
137
+ ]
138
+ }
139
+ }
140
+ ```
141
+
142
+ Built-in templates take priority, and external templates are supplemental. Between multiple external template sources, later sources override earlier sources. The sync report lists ignored same-path files in `templateSources.conflicts`. External templates and skills may contain scripts executed by AI workflows, so only configure trusted local paths.
143
+
123
144
  ## Custom Skills
124
145
 
125
146
  Projects can add their own skills alongside the built-in task workflow.
@@ -156,7 +177,7 @@ To reuse centralized team skills, configure `.agents/.airc.json`:
156
177
  {
157
178
  "skills": {
158
179
  "sources": [
159
- { "type": "local", "path": "~/company-skills" }
180
+ { "type": "local", "path": "~/private-skills" }
160
181
  ]
161
182
  }
162
183
  }
@@ -172,6 +193,54 @@ Each source should mirror the `.agents/skills/` layout and include `SKILL.md` at
172
193
  - Built-in skills are not overridable by custom sources; if a source skill name conflicts with a built-in skill, the source copy is skipped
173
194
  - Use `files.ejected` if the project must take ownership of a built-in skill or command
174
195
 
196
+ ## Custom TUI Configuration
197
+
198
+ Use the top-level `.agents/.airc.json` `customTUIs` array when your team uses an AI TUI that is not one of the built-in command targets. This config lets agent-infra show the correct next-step commands and generate command files for project custom skills by learning from an existing command in the custom TUI directory.
199
+
200
+ | Field | Required | Meaning |
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. |
204
+ | `invoke` | Yes | User-facing command template used in next-step guidance. |
205
+
206
+ Supported `invoke` placeholders:
207
+
208
+ | Placeholder | Replaced with | Example |
209
+ |-------------|---------------|---------|
210
+ | `${skillName}` | The skill command name, such as `review-task` or `commit`. | `acme ${skillName}` -> `acme review-task` |
211
+ | `${projectName}` | The `.airc.json` `project` value. Use this for namespaced commands. | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
212
+
213
+ Non-namespaced custom TUI:
214
+
215
+ ```json
216
+ {
217
+ "customTUIs": [
218
+ {
219
+ "name": "Acme TUI",
220
+ "dir": ".acme/commands",
221
+ "invoke": "acme ${skillName}"
222
+ }
223
+ ]
224
+ }
225
+ ```
226
+
227
+ Namespaced custom TUI:
228
+
229
+ ```json
230
+ {
231
+ "project": "agent-infra",
232
+ "customTUIs": [
233
+ {
234
+ "name": "Internal Gemini",
235
+ "dir": ".internal-gemini/commands",
236
+ "invoke": "/${projectName}:${skillName}"
237
+ }
238
+ ]
239
+ }
240
+ ```
241
+
242
+ `customTUIs` should contain one entry per custom TUI. To let `update-agent-infra` generate command files for custom skills, keep at least one existing command file in `dir` that references a built-in skill path such as `.agents/skills/analyze-task/SKILL.md`; agent-infra uses that file as the format reference.
243
+
175
244
  ## Skill Authoring Conventions
176
245
 
177
246
  When writing or updating `.agents/skills/*/SKILL.md` files and their templates, keep step numbering consistent:
@@ -120,6 +120,27 @@
120
120
  4. 如果你维护的是模板源码分支或私有 fork,需要先补齐对应的 `.{platform}.` 模板变体,再把该平台标识加入模板同步逻辑。
121
121
  5. 在正式推广前,先用一个测试任务完整验证工作流和 gate 校验。
122
122
 
123
+ ## 外部模板与 Skill 源
124
+
125
+ 团队可以在 `.agents/.airc.json` 中配置外部模板源和共享 skill 源,用于接入私有平台模板、私有规则和团队维护的自定义 skill:
126
+
127
+ ```json
128
+ {
129
+ "templates": {
130
+ "sources": [
131
+ { "type": "local", "path": "~/private-templates" }
132
+ ]
133
+ },
134
+ "skills": {
135
+ "sources": [
136
+ { "type": "local", "path": "~/private-skills" }
137
+ ]
138
+ }
139
+ }
140
+ ```
141
+
142
+ 模板源优先级为内置模板优先,外部模板只做补充;多个外部模板源之间,后面的 source 覆盖前面的 source。同步报告会在 `templateSources.conflicts` 中列出被忽略的同名文件。外部模板和 skill 可能包含会被 AI 工作流执行的脚本,只配置可信路径。
143
+
123
144
  ## 自定义 Skills
124
145
 
125
146
  项目可以在内置任务工作流之外增加自己的 skill。
@@ -156,7 +177,7 @@ args: "<task-id>" # 可选
156
177
  {
157
178
  "skills": {
158
179
  "sources": [
159
- { "type": "local", "path": "~/company-skills" }
180
+ { "type": "local", "path": "~/private-skills" }
160
181
  ]
161
182
  }
162
183
  }
@@ -172,6 +193,54 @@ args: "<task-id>" # 可选
172
193
  - 自定义 source 不能覆盖内置 skill;如果与内置 skill 同名,会跳过该 source skill
173
194
  - 如果项目必须接管某个内置 skill 或命令,请使用 `files.ejected`
174
195
 
196
+ ## 自定义 TUI 配置
197
+
198
+ 当团队使用的 AI TUI 不属于内置命令目标时,可以在 `.agents/.airc.json` 顶层配置 `customTUIs` 数组。该配置用于让 agent-infra 输出正确的下一步命令,并通过学习自定义 TUI 目录中的既有命令文件,为项目自定义 skill 生成同格式命令。
199
+
200
+ | 字段 | 必填 | 含义 |
201
+ |------|------|------|
202
+ | `name` | 是 | 报告和下一步提示中展示的工具名称,例如 `Acme TUI`。 |
203
+ | `dir` | 是 | 相对项目根目录的命令目录,例如 `.acme/commands`。路径必须位于项目根目录内。 |
204
+ | `invoke` | 是 | 面向用户展示的命令模板,用于生成下一步提示。 |
205
+
206
+ `invoke` 支持的占位符:
207
+
208
+ | 占位符 | 替换为 | 示例 |
209
+ |--------|--------|------|
210
+ | `${skillName}` | skill 命令名,例如 `review-task` 或 `commit`。 | `acme ${skillName}` -> `acme review-task` |
211
+ | `${projectName}` | `.airc.json` 中的 `project` 值,适用于带命名空间的命令。 | `/${projectName}:${skillName}` -> `/agent-infra:review-task` |
212
+
213
+ 不带命名空间的自定义 TUI:
214
+
215
+ ```json
216
+ {
217
+ "customTUIs": [
218
+ {
219
+ "name": "Acme TUI",
220
+ "dir": ".acme/commands",
221
+ "invoke": "acme ${skillName}"
222
+ }
223
+ ]
224
+ }
225
+ ```
226
+
227
+ 带命名空间的自定义 TUI:
228
+
229
+ ```json
230
+ {
231
+ "project": "agent-infra",
232
+ "customTUIs": [
233
+ {
234
+ "name": "Internal Gemini",
235
+ "dir": ".internal-gemini/commands",
236
+ "invoke": "/${projectName}:${skillName}"
237
+ }
238
+ ]
239
+ }
240
+ ```
241
+
242
+ `customTUIs` 每个条目对应一个自定义 TUI。若希望 `update-agent-infra` 为自定义 skill 生成命令文件,请在 `dir` 中保留至少一个引用内置 skill 路径的既有命令文件,例如 `.agents/skills/analyze-task/SKILL.md`;agent-infra 会以该文件作为格式参考。
243
+
175
244
  ## Skill 编写规范
176
245
 
177
246
  编写或维护 `.agents/skills/*/SKILL.md` 及其模板时,步骤编号遵循以下规则:
@@ -0,0 +1,5 @@
1
+ # Issue and PR Commands
2
+
3
+ This code platform does not provide built-in issue or pull request commands.
4
+
5
+ Platform-specific automation is skipped for custom platforms unless you provide matching `.{platform}.en.md` rule templates. Keep local task artifacts as the source of truth, or install a platform-specific template pack before running the workflow.
@@ -0,0 +1,5 @@
1
+ # Issue 和 PR 命令
2
+
3
+ 当前代码平台未内置 Issue 或 Pull Request 命令支持。
4
+
5
+ 自定义平台会跳过平台专属自动化,除非你提供匹配的 `.{platform}.zh-CN.md` 规则模板。请以本地任务产物作为事实来源,或先安装平台专属模板包再运行工作流。
@@ -0,0 +1,5 @@
1
+ # Issue Sync
2
+
3
+ This code platform does not provide built-in issue synchronization.
4
+
5
+ Issue metadata, labels, milestones, assignees, and comments are skipped for custom platforms unless you provide matching `.{platform}.en.md` rule templates and platform adapters. Continue writing local task artifacts normally.
@@ -0,0 +1,5 @@
1
+ # Issue 同步
2
+
3
+ 当前代码平台未内置 Issue 同步支持。
4
+
5
+ 自定义平台会跳过 Issue 元数据、标签、里程碑、负责人和评论同步,除非你提供匹配的 `.{platform}.zh-CN.md` 规则模板和平台适配器。请继续正常写入本地任务产物。
@@ -0,0 +1,5 @@
1
+ # Label and Milestone Setup
2
+
3
+ This code platform does not provide built-in label or milestone setup.
4
+
5
+ Initialization of remote labels and milestones is skipped for custom platforms unless you provide matching `.{platform}.en.md` rule templates and scripts. Local task workflow files remain usable without remote metadata setup.
@@ -0,0 +1,5 @@
1
+ # Label 和 Milestone 初始化
2
+
3
+ 当前代码平台未内置标签或里程碑初始化支持。
4
+
5
+ 自定义平台会跳过远端标签和里程碑初始化,除非你提供匹配的 `.{platform}.zh-CN.md` 规则模板和脚本。即使没有远端元数据初始化,本地任务工作流文件仍可使用。
@@ -0,0 +1,5 @@
1
+ # Milestone Inference
2
+
3
+ This code platform does not provide built-in milestone inference.
4
+
5
+ Milestone narrowing and reuse are skipped for custom platforms unless you provide matching `.{platform}.en.md` rule templates. Do not block task progress when no platform-specific milestone implementation is available.
@@ -0,0 +1,5 @@
1
+ # Milestone 推断
2
+
3
+ 当前代码平台未内置里程碑推断支持。
4
+
5
+ 自定义平台会跳过里程碑收窄和复用逻辑,除非你提供匹配的 `.{platform}.zh-CN.md` 规则模板。没有平台专属里程碑实现时,不要阻塞任务推进。
@@ -0,0 +1,5 @@
1
+ # PR Sync
2
+
3
+ This code platform does not provide built-in pull request synchronization.
4
+
5
+ PR creation, metadata updates, labels, milestones, assignees, and summary comments are skipped for custom platforms unless you provide matching `.{platform}.en.md` rule templates and adapters. Keep local task artifacts up to date.
@@ -0,0 +1,5 @@
1
+ # PR 同步
2
+
3
+ 当前代码平台未内置 Pull Request 同步支持。
4
+
5
+ 自定义平台会跳过 PR 创建、元数据更新、标签、里程碑、负责人和摘要评论同步,除非你提供匹配的 `.{platform}.zh-CN.md` 规则模板和适配器。请保持本地任务产物更新。
@@ -0,0 +1,5 @@
1
+ # Release Commands
2
+
3
+ This code platform does not provide built-in release commands.
4
+
5
+ Remote release automation is skipped for custom platforms unless you provide matching `.{platform}.en.md` rule templates and scripts. Continue using local release artifacts or your platform-specific release process.
@@ -0,0 +1,5 @@
1
+ # Release 命令
2
+
3
+ 当前代码平台未内置发布命令支持。
4
+
5
+ 自定义平台会跳过远端发布自动化,除非你提供匹配的 `.{platform}.zh-CN.md` 规则模板和脚本。请继续使用本地发布产物或你的平台专属发布流程。
@@ -0,0 +1,5 @@
1
+ # Security Alerts
2
+
3
+ This code platform does not provide built-in security alert integration.
4
+
5
+ Code scanning and dependency alert import or close actions are skipped for custom platforms unless you provide matching `.{platform}.en.md` rule templates and adapters. Track security work through local task artifacts or a platform-specific integration.
@@ -0,0 +1,5 @@
1
+ # 安全告警
2
+
3
+ 当前代码平台未内置安全告警集成。
4
+
5
+ 自定义平台会跳过代码扫描和依赖告警的导入或关闭操作,除非你提供匹配的 `.{platform}.zh-CN.md` 规则模板和适配器。请通过本地任务产物或平台专属集成跟踪安全工作。
@@ -0,0 +1,6 @@
1
+ export function check(_context, shared) {
2
+ return shared.passResult(
3
+ "platform-sync",
4
+ "Skipped: this code platform does not provide a built-in platform-sync adapter"
5
+ );
6
+ }
@@ -143,7 +143,7 @@ Keep the gate output in your reply as fresh evidence. Do not claim completion wi
143
143
 
144
144
  > Execute this step only after the verification gate passes.
145
145
 
146
- > **IMPORTANT**: All TUI command formats listed below must be output in full. Do not show only the format for the current AI agent.
146
+ > **IMPORTANT**: All TUI command formats listed below must be output in full. Do not show only the format for the current AI agent. If `.agents/.airc.json` configures custom TUIs (via `customTUIs`), read each tool's `name` and `invoke`, then add the matching command line in the same format (`${skillName}` becomes the skill name and `${projectName}` becomes the project name).
147
147
 
148
148
  Output format:
149
149
  ```
@@ -172,7 +172,7 @@ Next step - create technical plan:
172
172
  - [ ] Updated `assigned_to` in task.md
173
173
  - [ ] Appended an Activity Log entry to task.md
174
174
  - [ ] Marked requirement-analysis as complete in workflow progress
175
- - [ ] Informed the user of the next step (must include all TUI command formats; do not filter)
175
+ - [ ] Informed the user of the next step (must include all TUI command formats, including any custom TUIs; do not filter)
176
176
  - [ ] **Did not modify any business code**
177
177
 
178
178
  ## STOP
@@ -143,7 +143,7 @@ node .agents/scripts/validate-artifact.js gate analyze-task .agents/workspace/ac
143
143
 
144
144
  > 仅在校验通过后执行本步骤。
145
145
 
146
- > **重要**:以下「下一步」中列出的所有 TUI 命令格式必须完整输出,不要只展示当前 AI 代理对应的格式。
146
+ > **重要**:以下「下一步」中列出的所有 TUI 命令格式必须完整输出,不要只展示当前 AI 代理对应的格式。如果 `.agents/.airc.json` 中配置了自定义 TUI(`customTUIs`),读取每个工具的 `name` 和 `invoke`,按同样格式补充对应命令行(`${skillName}` 替换为技能名,`${projectName}` 替换为项目名)。
147
147
 
148
148
  输出格式:
149
149
  ```
@@ -172,7 +172,7 @@ node .agents/scripts/validate-artifact.js gate analyze-task .agents/workspace/ac
172
172
  - [ ] 更新了 task.md 中的 `assigned_to`
173
173
  - [ ] 追加了 Activity Log 条目到 task.md
174
174
  - [ ] 在工作流进度中标记了 requirement-analysis 为已完成
175
- - [ ] 告知了用户下一步(必须展示所有 TUI 的命令格式,不要筛选)
175
+ - [ ] 告知了用户下一步(必须展示所有 TUI 的命令格式,含自定义 TUI,不要筛选)
176
176
  - [ ] **没有修改任何业务代码**
177
177
 
178
178
  ## 停止
@@ -93,7 +93,7 @@ Keep the gate output in your reply as fresh evidence. Do not claim completion wi
93
93
 
94
94
  > Execute this step only after the verification gate passes.
95
95
 
96
- > **IMPORTANT**: All TUI command formats listed below must be output in full. Do not show only the format for the current AI agent.
96
+ > **IMPORTANT**: All TUI command formats listed below must be output in full. Do not show only the format for the current AI agent. If `.agents/.airc.json` configures custom TUIs (via `customTUIs`), read each tool's `name` and `invoke`, then add the matching command line in the same format (`${skillName}` becomes the skill name and `${projectName}` becomes the project name).
97
97
 
98
98
  Output format:
99
99
  ```
@@ -93,7 +93,7 @@ node .agents/scripts/validate-artifact.js gate block-task .agents/workspace/bloc
93
93
 
94
94
  > 仅在校验通过后执行本步骤。
95
95
 
96
- > **重要**:以下「下一步」中列出的所有 TUI 命令格式必须完整输出,不要只展示当前 AI 代理对应的格式。
96
+ > **重要**:以下「下一步」中列出的所有 TUI 命令格式必须完整输出,不要只展示当前 AI 代理对应的格式。如果 `.agents/.airc.json` 中配置了自定义 TUI(`customTUIs`),读取每个工具的 `name` 和 `invoke`,按同样格式补充对应命令行(`${skillName}` 替换为技能名,`${projectName}` 替换为项目名)。
97
97
 
98
98
  输出格式:
99
99
  ```
@@ -106,7 +106,7 @@ Keep the gate output in your reply as fresh evidence. Do not claim completion wi
106
106
 
107
107
  > Execute this step only after the verification gate passes.
108
108
 
109
- > **IMPORTANT**: All TUI command formats listed below must be output in full. Do not show only the format for the current AI agent.
109
+ > **IMPORTANT**: All TUI command formats listed below must be output in full. Do not show only the format for the current AI agent. If `.agents/.airc.json` configures custom TUIs (via `customTUIs`), read each tool's `name` and `invoke`, then add the matching command line in the same format (`${skillName}` becomes the skill name and `${projectName}` becomes the project name).
110
110
 
111
111
  Output format:
112
112
  ```