@fitlab-ai/agent-infra 0.9.1 → 0.9.2

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 (283) hide show
  1. package/README.md +16 -4
  2. package/README.zh-CN.md +16 -4
  3. package/bin/cli.ts +12 -0
  4. package/dist/bin/cli.js +14 -0
  5. package/dist/bin/internal-cli.js +15 -0
  6. package/dist/lib/agent-client.js +163 -0
  7. package/dist/lib/agent-clients/adapter.js +294 -0
  8. package/dist/lib/agent-clients/adapters/claude-code.js +62 -0
  9. package/dist/lib/agent-clients/adapters/codex-sandbox.js +210 -0
  10. package/dist/lib/agent-clients/adapters/codex.js +54 -0
  11. package/dist/lib/agent-clients/adapters/gemini-cli.js +64 -0
  12. package/dist/lib/agent-clients/adapters/index.js +20 -0
  13. package/dist/lib/agent-clients/adapters/opencode.js +64 -0
  14. package/dist/lib/agent-clients/config.js +177 -0
  15. package/dist/lib/agent-clients/custom-tuis.js +99 -0
  16. package/dist/lib/agent-clients/invocation.js +9 -0
  17. package/dist/lib/agent-clients/next-steps.js +38 -0
  18. package/dist/lib/agent-clients/project-assets.js +45 -0
  19. package/dist/lib/agent-clients/reconcile.js +235 -0
  20. package/dist/lib/agent-clients/registry.js +72 -0
  21. package/dist/lib/agent-clients/schema.js +57 -0
  22. package/dist/lib/agent-clients/types.js +29 -0
  23. package/dist/lib/builtin-tuis.js +9 -14
  24. package/dist/lib/defaults.json +85 -73
  25. package/dist/lib/git/review-snapshot.js +25 -10
  26. package/dist/lib/git/reviewed-commit-equivalence.js +110 -0
  27. package/dist/lib/init.js +47 -91
  28. package/dist/lib/internal/agent-client.js +117 -0
  29. package/dist/lib/internal/git-workflow.js +1 -1
  30. package/dist/lib/internal/release-workflow.js +71 -16
  31. package/dist/lib/internal/task-orchestration.js +151 -0
  32. package/dist/lib/internal/task-review.js +65 -0
  33. package/dist/lib/merge.js +2 -1
  34. package/dist/lib/platform/adapters.js +38 -1
  35. package/dist/lib/platform/change-request-git-evidence.js +113 -0
  36. package/dist/lib/platform/merged-pr-equivalence.js +41 -11
  37. package/dist/lib/platform/pr-checks.js +38 -10
  38. package/dist/lib/platform/pull-requests.js +110 -6
  39. package/dist/lib/platform/releases.js +1 -1
  40. package/dist/lib/platform/verification-required.js +8 -10
  41. package/dist/lib/prompt.js +15 -3
  42. package/dist/lib/release/channels.js +4 -1
  43. package/dist/lib/release/workflow.js +4 -2
  44. package/dist/lib/remove-dir.js +25 -0
  45. package/dist/lib/run/tui.js +8 -2
  46. package/dist/lib/sandbox/agent-client-reconciler.js +234 -0
  47. package/dist/lib/sandbox/clipboard/linux.js +2 -1
  48. package/dist/lib/sandbox/commands/create.js +76 -180
  49. package/dist/lib/sandbox/commands/enter.js +17 -1
  50. package/dist/lib/sandbox/commands/prune.js +2 -2
  51. package/dist/lib/sandbox/commands/rm.js +3 -2
  52. package/dist/lib/sandbox/config.js +21 -4
  53. package/dist/lib/sandbox/constants.js +3 -0
  54. package/dist/lib/sandbox/dockerfile.js +2 -1
  55. package/dist/lib/sandbox/managed-fs.js +10 -5
  56. package/dist/lib/sandbox/recovery.js +52 -36
  57. package/dist/lib/sandbox/shell.js +1 -1
  58. package/dist/lib/sandbox/tool-types.js +8 -0
  59. package/dist/lib/sandbox/tools.js +2 -126
  60. package/dist/lib/task/artifact-lifecycle.js +1 -49
  61. package/dist/lib/task/commands/decisions.js +50 -20
  62. package/dist/lib/task/delegation-receipts.js +107 -0
  63. package/dist/lib/task/events.js +100 -1
  64. package/dist/lib/task/orchestration.js +420 -0
  65. package/dist/lib/task/review-artifacts.js +143 -59
  66. package/dist/lib/task/review-finalization.js +166 -0
  67. package/dist/lib/task/review-fingerprint.js +4 -0
  68. package/dist/lib/task/verification-checks.js +2 -1
  69. package/dist/lib/task/verification-engine.js +156 -28
  70. package/dist/lib/task/verification-review.js +1 -1
  71. package/dist/lib/task/verification.js +3 -1
  72. package/dist/lib/task/workspace-snapshot.js +34 -0
  73. package/dist/lib/update.js +42 -161
  74. package/lib/agent-client.ts +214 -0
  75. package/lib/agent-clients/adapter.ts +461 -0
  76. package/lib/agent-clients/adapters/claude-code.ts +70 -0
  77. package/lib/agent-clients/adapters/codex-sandbox.ts +245 -0
  78. package/lib/agent-clients/adapters/codex.ts +58 -0
  79. package/lib/agent-clients/adapters/gemini-cli.ts +65 -0
  80. package/lib/agent-clients/adapters/index.ts +27 -0
  81. package/lib/agent-clients/adapters/opencode.ts +66 -0
  82. package/lib/agent-clients/config.ts +278 -0
  83. package/lib/agent-clients/custom-tuis.ts +149 -0
  84. package/lib/agent-clients/invocation.ts +19 -0
  85. package/lib/agent-clients/next-steps.ts +72 -0
  86. package/lib/agent-clients/project-assets.ts +96 -0
  87. package/lib/agent-clients/reconcile.ts +324 -0
  88. package/lib/agent-clients/registry.ts +144 -0
  89. package/lib/agent-clients/schema.ts +62 -0
  90. package/lib/agent-clients/types.ts +78 -0
  91. package/lib/benchmark/schemas/v1.0.0/case-manifest.schema.json +120 -0
  92. package/lib/benchmark/schemas/v1.0.0/common.schema.json +109 -0
  93. package/lib/benchmark/schemas/v1.0.0/grader-result.schema.json +160 -0
  94. package/lib/benchmark/schemas/v1.0.0/run-manifest.schema.json +122 -0
  95. package/lib/benchmark/schemas/v1.0.0/subject.schema.json +227 -0
  96. package/lib/builtin-tuis.ts +16 -15
  97. package/lib/defaults.json +24 -12
  98. package/lib/git/review-snapshot.ts +39 -9
  99. package/lib/git/reviewed-commit-equivalence.ts +135 -0
  100. package/lib/init.ts +53 -121
  101. package/lib/internal/agent-client.ts +131 -0
  102. package/lib/internal/git-workflow.ts +1 -1
  103. package/lib/internal/release-workflow.ts +68 -16
  104. package/lib/internal/task-orchestration.ts +132 -0
  105. package/lib/internal/task-review.ts +65 -0
  106. package/lib/merge.ts +2 -1
  107. package/lib/platform/adapters.ts +142 -3
  108. package/lib/platform/change-request-git-evidence.ts +159 -0
  109. package/lib/platform/merged-pr-equivalence.ts +51 -14
  110. package/lib/platform/pr-checks.ts +50 -22
  111. package/lib/platform/pull-requests.ts +134 -25
  112. package/lib/platform/releases.ts +1 -1
  113. package/lib/platform/verification-required.ts +12 -10
  114. package/lib/prompt.ts +16 -3
  115. package/lib/release/channels.ts +4 -1
  116. package/lib/release/workflow.ts +4 -3
  117. package/lib/remove-dir.ts +28 -0
  118. package/lib/run/tui.ts +9 -1
  119. package/lib/sandbox/agent-client-reconciler.ts +378 -0
  120. package/lib/sandbox/clipboard/linux.ts +2 -1
  121. package/lib/sandbox/commands/create.ts +111 -192
  122. package/lib/sandbox/commands/enter.ts +23 -1
  123. package/lib/sandbox/commands/prune.ts +2 -2
  124. package/lib/sandbox/commands/rm.ts +3 -2
  125. package/lib/sandbox/config.ts +31 -4
  126. package/lib/sandbox/constants.ts +6 -0
  127. package/lib/sandbox/dockerfile.ts +2 -1
  128. package/lib/sandbox/managed-fs.ts +18 -5
  129. package/lib/sandbox/recovery.ts +98 -67
  130. package/lib/sandbox/shell.ts +1 -1
  131. package/lib/sandbox/tool-types.ts +143 -0
  132. package/lib/sandbox/tools.ts +5 -157
  133. package/lib/task/artifact-lifecycle.ts +1 -38
  134. package/lib/task/commands/decisions.ts +53 -20
  135. package/lib/task/delegation-receipts.ts +170 -0
  136. package/lib/task/events.ts +110 -2
  137. package/lib/task/orchestration.ts +543 -0
  138. package/lib/task/review-artifacts.ts +201 -69
  139. package/lib/task/review-finalization.ts +275 -0
  140. package/lib/task/review-fingerprint.ts +5 -0
  141. package/lib/task/verification-checks.ts +2 -1
  142. package/lib/task/verification-engine.ts +177 -28
  143. package/lib/task/verification-review.ts +1 -0
  144. package/lib/task/verification.ts +5 -2
  145. package/lib/task/workspace-snapshot.ts +36 -0
  146. package/lib/update.ts +48 -184
  147. package/package.json +2 -2
  148. package/templates/.agents/README.en.md +21 -20
  149. package/templates/.agents/README.zh-CN.md +21 -20
  150. package/templates/.agents/hooks/lifecycle-delegation.js +64 -0
  151. package/templates/.agents/rules/README.en.md +2 -0
  152. package/templates/.agents/rules/README.zh-CN.md +2 -0
  153. package/templates/.agents/rules/commit-and-pr.en.md +1 -0
  154. package/templates/.agents/rules/commit-and-pr.zh-CN.md +1 -0
  155. package/templates/.agents/rules/human-decision-context.en.md +14 -12
  156. package/templates/.agents/rules/human-decision-context.zh-CN.md +14 -12
  157. package/templates/.agents/rules/lifecycle-orchestration.en.md +21 -0
  158. package/templates/.agents/rules/lifecycle-orchestration.zh-CN.md +21 -0
  159. package/templates/.agents/rules/next-step-output.en.md +16 -0
  160. package/templates/.agents/rules/next-step-output.zh-CN.md +16 -0
  161. package/templates/.agents/rules/no-mid-flow-questions.en.md +2 -0
  162. package/templates/.agents/rules/no-mid-flow-questions.zh-CN.md +2 -0
  163. package/templates/.agents/rules/release-commands.github.en.md +11 -5
  164. package/templates/.agents/rules/release-commands.github.zh-CN.md +11 -5
  165. package/templates/.agents/rules/review-handshake.en.md +4 -4
  166. package/templates/.agents/rules/review-handshake.zh-CN.md +4 -4
  167. package/templates/.agents/rules/review-method.en.md +113 -0
  168. package/templates/.agents/rules/review-method.zh-CN.md +98 -0
  169. package/templates/.agents/skills/analyze-task/SKILL.en.md +5 -5
  170. package/templates/.agents/skills/analyze-task/SKILL.zh-CN.md +5 -5
  171. package/templates/.agents/skills/block-task/SKILL.en.md +4 -4
  172. package/templates/.agents/skills/block-task/SKILL.zh-CN.md +4 -4
  173. package/templates/.agents/skills/cancel-task/SKILL.en.md +4 -4
  174. package/templates/.agents/skills/cancel-task/SKILL.zh-CN.md +5 -5
  175. package/templates/.agents/skills/check-task/SKILL.en.md +16 -16
  176. package/templates/.agents/skills/check-task/SKILL.zh-CN.md +16 -16
  177. package/templates/.agents/skills/close-codescan/SKILL.en.md +4 -4
  178. package/templates/.agents/skills/close-codescan/SKILL.zh-CN.md +4 -4
  179. package/templates/.agents/skills/close-dependabot/SKILL.en.md +4 -4
  180. package/templates/.agents/skills/close-dependabot/SKILL.zh-CN.md +4 -4
  181. package/templates/.agents/skills/code-task/SKILL.en.md +1 -1
  182. package/templates/.agents/skills/code-task/SKILL.zh-CN.md +2 -2
  183. package/templates/.agents/skills/code-task/reference/fix-mode.en.md +14 -16
  184. package/templates/.agents/skills/code-task/reference/fix-mode.zh-CN.md +14 -16
  185. package/templates/.agents/skills/code-task/reference/output-template.en.md +3 -3
  186. package/templates/.agents/skills/code-task/reference/output-template.zh-CN.md +3 -3
  187. package/templates/.agents/skills/commit/SKILL.en.md +11 -1
  188. package/templates/.agents/skills/commit/SKILL.zh-CN.md +11 -1
  189. package/templates/.agents/skills/commit/reference/task-status-update.en.md +24 -28
  190. package/templates/.agents/skills/commit/reference/task-status-update.zh-CN.md +24 -28
  191. package/templates/.agents/skills/complete-task/SKILL.en.md +2 -2
  192. package/templates/.agents/skills/complete-task/SKILL.zh-CN.md +2 -2
  193. package/templates/.agents/skills/create-pr/SKILL.en.md +10 -10
  194. package/templates/.agents/skills/create-pr/SKILL.zh-CN.md +10 -10
  195. package/templates/.agents/skills/create-pr/reference/pr-body-template.en.md +3 -3
  196. package/templates/.agents/skills/create-pr/reference/pr-body-template.zh-CN.md +3 -3
  197. package/templates/.agents/skills/create-task/SKILL.en.md +11 -11
  198. package/templates/.agents/skills/create-task/SKILL.zh-CN.md +11 -11
  199. package/templates/.agents/skills/import-codescan/SKILL.en.md +5 -5
  200. package/templates/.agents/skills/import-codescan/SKILL.zh-CN.md +5 -5
  201. package/templates/.agents/skills/import-dependabot/SKILL.en.md +5 -5
  202. package/templates/.agents/skills/import-dependabot/SKILL.zh-CN.md +5 -5
  203. package/templates/.agents/skills/import-issue/SKILL.en.md +5 -5
  204. package/templates/.agents/skills/import-issue/SKILL.zh-CN.md +5 -5
  205. package/templates/.agents/skills/init-labels/SKILL.en.md +4 -4
  206. package/templates/.agents/skills/init-labels/SKILL.zh-CN.md +4 -4
  207. package/templates/.agents/skills/init-milestones/SKILL.en.md +4 -4
  208. package/templates/.agents/skills/init-milestones/SKILL.zh-CN.md +4 -4
  209. package/templates/.agents/skills/plan-task/SKILL.en.md +5 -5
  210. package/templates/.agents/skills/plan-task/SKILL.zh-CN.md +5 -5
  211. package/templates/.agents/skills/refine-title/SKILL.en.md +1 -1
  212. package/templates/.agents/skills/refine-title/SKILL.zh-CN.md +1 -1
  213. package/templates/.agents/skills/release/SKILL.en.md +1 -1
  214. package/templates/.agents/skills/review-analysis/SKILL.en.md +13 -2
  215. package/templates/.agents/skills/review-analysis/SKILL.zh-CN.md +14 -3
  216. package/templates/.agents/skills/review-analysis/config/verify.en.json +6 -0
  217. package/templates/.agents/skills/review-analysis/config/verify.zh-CN.json +6 -0
  218. package/templates/.agents/skills/review-analysis/reference/output-templates.en.md +13 -15
  219. package/templates/.agents/skills/review-analysis/reference/output-templates.zh-CN.md +13 -15
  220. package/templates/.agents/skills/review-analysis/reference/report-template.en.md +51 -5
  221. package/templates/.agents/skills/review-analysis/reference/report-template.zh-CN.md +52 -6
  222. package/templates/.agents/skills/review-analysis/reference/review-criteria.en.md +25 -22
  223. package/templates/.agents/skills/review-analysis/reference/review-criteria.zh-CN.md +25 -22
  224. package/templates/.agents/skills/review-code/SKILL.en.md +28 -8
  225. package/templates/.agents/skills/review-code/SKILL.zh-CN.md +28 -8
  226. package/templates/.agents/skills/review-code/config/verify.en.json +7 -0
  227. package/templates/.agents/skills/review-code/config/verify.zh-CN.json +7 -0
  228. package/templates/.agents/skills/review-code/reference/concurrency-risks.en.md +25 -0
  229. package/templates/.agents/skills/review-code/reference/concurrency-risks.zh-CN.md +25 -0
  230. package/templates/.agents/skills/review-code/reference/cross-platform-risks.en.md +27 -0
  231. package/templates/.agents/skills/review-code/reference/cross-platform-risks.zh-CN.md +27 -0
  232. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.en.md +13 -0
  233. package/templates/.agents/skills/review-code/reference/documentation-antipatterns.zh-CN.md +13 -0
  234. package/templates/.agents/skills/review-code/reference/migration-risks.en.md +25 -0
  235. package/templates/.agents/skills/review-code/reference/migration-risks.zh-CN.md +25 -0
  236. package/templates/.agents/skills/review-code/reference/output-templates.en.md +22 -24
  237. package/templates/.agents/skills/review-code/reference/output-templates.zh-CN.md +22 -24
  238. package/templates/.agents/skills/review-code/reference/report-template.en.md +54 -7
  239. package/templates/.agents/skills/review-code/reference/report-template.zh-CN.md +54 -7
  240. package/templates/.agents/skills/review-code/reference/review-criteria.en.md +29 -24
  241. package/templates/.agents/skills/review-code/reference/review-criteria.zh-CN.md +29 -24
  242. package/templates/.agents/skills/review-code/reference/security-risks.en.md +25 -0
  243. package/templates/.agents/skills/review-code/reference/security-risks.zh-CN.md +25 -0
  244. package/templates/.agents/skills/review-plan/SKILL.en.md +13 -2
  245. package/templates/.agents/skills/review-plan/SKILL.zh-CN.md +14 -3
  246. package/templates/.agents/skills/review-plan/config/verify.en.json +6 -0
  247. package/templates/.agents/skills/review-plan/config/verify.zh-CN.json +6 -0
  248. package/templates/.agents/skills/review-plan/reference/output-templates.en.md +13 -15
  249. package/templates/.agents/skills/review-plan/reference/output-templates.zh-CN.md +13 -15
  250. package/templates/.agents/skills/review-plan/reference/report-template.en.md +61 -5
  251. package/templates/.agents/skills/review-plan/reference/report-template.zh-CN.md +61 -5
  252. package/templates/.agents/skills/review-plan/reference/review-criteria.en.md +21 -24
  253. package/templates/.agents/skills/review-plan/reference/review-criteria.zh-CN.md +21 -24
  254. package/templates/.agents/skills/run-task/SKILL.en.md +22 -0
  255. package/templates/.agents/skills/run-task/SKILL.zh-CN.md +22 -0
  256. package/templates/.agents/skills/run-task/config/verify.en.json +9 -0
  257. package/templates/.agents/skills/run-task/config/verify.zh-CN.json +9 -0
  258. package/templates/.agents/skills/test/SKILL.en.md +4 -4
  259. package/templates/.agents/skills/test/SKILL.zh-CN.md +4 -4
  260. package/templates/.agents/skills/test-integration/SKILL.en.md +4 -4
  261. package/templates/.agents/skills/test-integration/SKILL.zh-CN.md +4 -4
  262. package/templates/.agents/skills/update-agent-infra/SKILL.en.md +5 -5
  263. package/templates/.agents/skills/update-agent-infra/SKILL.zh-CN.md +5 -5
  264. package/templates/.agents/skills/update-agent-infra/scripts/sync-templates.js +647 -203
  265. package/templates/.agents/skills/upgrade-dependency/SKILL.en.md +4 -4
  266. package/templates/.agents/skills/upgrade-dependency/SKILL.zh-CN.md +4 -4
  267. package/templates/.agents/skills/watch-pr/SKILL.en.md +8 -8
  268. package/templates/.agents/skills/watch-pr/SKILL.zh-CN.md +8 -8
  269. package/templates/.claude/agents/agent-infra-lifecycle-executor.en.md +6 -0
  270. package/templates/.claude/agents/agent-infra-lifecycle-executor.zh-CN.md +6 -0
  271. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.en.md +6 -0
  272. package/templates/.claude/agents/agent-infra-lifecycle-reviewer.zh-CN.md +6 -0
  273. package/templates/.claude/commands/run-task.en.md +8 -0
  274. package/templates/.claude/commands/run-task.zh-CN.md +8 -0
  275. package/templates/.claude/settings.json +24 -0
  276. package/templates/.codex/agents/agent-infra-lifecycle-executor.en.toml +3 -0
  277. package/templates/.codex/agents/agent-infra-lifecycle-executor.zh-CN.toml +3 -0
  278. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.en.toml +3 -0
  279. package/templates/.codex/agents/agent-infra-lifecycle-reviewer.zh-CN.toml +3 -0
  280. package/templates/.gemini/commands/_project_/run-task.en.toml +8 -0
  281. package/templates/.gemini/commands/_project_/run-task.zh-CN.toml +8 -0
  282. package/templates/.opencode/commands/run-task.en.md +11 -0
  283. package/templates/.opencode/commands/run-task.zh-CN.md +11 -0
@@ -0,0 +1,99 @@
1
+ import path from 'node:path';
2
+ const CUSTOM_TUI_CONTRACT = Object.freeze({
3
+ requiredFields: Object.freeze(['name', 'dir', 'invoke']),
4
+ allowedPlaceholders: Object.freeze(['skillName', 'projectName'])
5
+ });
6
+ function isRecord(value) {
7
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
8
+ }
9
+ function isNonEmptySingleLine(value) {
10
+ return typeof value === 'string'
11
+ && value.trim() !== ''
12
+ && !/[\r\n]/.test(value);
13
+ }
14
+ function normalizeDir(projectRoot, value) {
15
+ if (!isNonEmptySingleLine(value)
16
+ || value.includes('\\')
17
+ || path.isAbsolute(value)
18
+ || /^[a-zA-Z]:/.test(value)) {
19
+ return null;
20
+ }
21
+ const normalized = path.posix.normalize(value);
22
+ if (normalized === '.'
23
+ || normalized === '..'
24
+ || normalized.startsWith('../')
25
+ || normalized.split('/').includes('..')) {
26
+ return null;
27
+ }
28
+ const relative = path.relative(path.resolve(projectRoot), path.resolve(projectRoot, normalized));
29
+ return relative === ''
30
+ || path.isAbsolute(relative)
31
+ || relative === '..'
32
+ || relative.startsWith(`..${path.sep}`)
33
+ ? null
34
+ : normalized;
35
+ }
36
+ function hasValidPlaceholders(invocation) {
37
+ const placeholders = [
38
+ ...invocation.matchAll(/\$\{([^}]+)\}/g)
39
+ ].map((match) => match[1]);
40
+ return placeholders.includes('skillName')
41
+ && placeholders.every((placeholder) => CUSTOM_TUI_CONTRACT.allowedPlaceholders.includes(placeholder))
42
+ && !invocation
43
+ .replaceAll('${skillName}', '')
44
+ .replaceAll('${projectName}', '')
45
+ .includes('${');
46
+ }
47
+ function normalizeCustomTUIs(projectRoot, input) {
48
+ const items = [];
49
+ const diagnostics = [];
50
+ if (!Array.isArray(input)) {
51
+ return Object.freeze({
52
+ items: Object.freeze([]),
53
+ diagnostics: Object.freeze([
54
+ Object.freeze({
55
+ code: 'INVALID_CUSTOM_TUIS',
56
+ path: 'customTUIs'
57
+ })
58
+ ])
59
+ });
60
+ }
61
+ for (const [index, candidate] of input.entries()) {
62
+ const base = `customTUIs[${index}]`;
63
+ if (!isRecord(candidate)) {
64
+ diagnostics.push({ code: 'INVALID_CUSTOM_TUI', path: base });
65
+ continue;
66
+ }
67
+ if (!isNonEmptySingleLine(candidate.name)) {
68
+ diagnostics.push({ code: 'INVALID_CUSTOM_TUI', path: `${base}.name` });
69
+ continue;
70
+ }
71
+ const dir = normalizeDir(projectRoot, candidate.dir);
72
+ if (!dir) {
73
+ diagnostics.push({ code: 'INVALID_CUSTOM_TUI', path: `${base}.dir` });
74
+ continue;
75
+ }
76
+ if (!isNonEmptySingleLine(candidate.invoke)) {
77
+ diagnostics.push({ code: 'INVALID_CUSTOM_TUI', path: `${base}.invoke` });
78
+ continue;
79
+ }
80
+ if (!hasValidPlaceholders(candidate.invoke)) {
81
+ diagnostics.push({
82
+ code: 'INVALID_CUSTOM_TUI_PLACEHOLDER',
83
+ path: `${base}.invoke`
84
+ });
85
+ continue;
86
+ }
87
+ items.push(Object.freeze({
88
+ name: candidate.name,
89
+ dir,
90
+ invocation: candidate.invoke
91
+ }));
92
+ }
93
+ return Object.freeze({
94
+ items: Object.freeze(items),
95
+ diagnostics: Object.freeze(diagnostics.map((diagnostic) => Object.freeze(diagnostic)))
96
+ });
97
+ }
98
+ export { CUSTOM_TUI_CONTRACT, normalizeCustomTUIs };
99
+ //# sourceMappingURL=custom-tuis.js.map
@@ -0,0 +1,9 @@
1
+ function renderAgentClientInvocation(template, input) {
2
+ let command = template.replaceAll('${skillName}', input.skillName);
3
+ if (input.projectName !== undefined) {
4
+ command = command.replaceAll('${projectName}', input.projectName);
5
+ }
6
+ return [command, ...(input.args ?? [])].join(' ').trim();
7
+ }
8
+ export { renderAgentClientInvocation };
9
+ //# sourceMappingURL=invocation.js.map
@@ -0,0 +1,38 @@
1
+ import { listEnabledAgentClientAdapters } from './registry.js';
2
+ import { renderAgentClientInvocation } from './invocation.js';
3
+ const SAFE_NAME = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
4
+ const SAFE_TASK_REF = /^(?:\d+|TASK-\d{8}-\d{6})$/;
5
+ function requireSafeName(value, field) {
6
+ if (!SAFE_NAME.test(value)) {
7
+ throw new Error(`Invalid ${field}`);
8
+ }
9
+ }
10
+ function renderNextStepCommands(input) {
11
+ requireSafeName(input.projectName, 'project name');
12
+ requireSafeName(input.skillName, 'skill name');
13
+ if (input.taskRef !== undefined && !SAFE_TASK_REF.test(input.taskRef)) {
14
+ throw new Error('Invalid task ref');
15
+ }
16
+ const builtins = listEnabledAgentClientAdapters(input.state).map((adapter) => Object.freeze({
17
+ source: 'builtin',
18
+ clientId: adapter.id,
19
+ displayName: adapter.displayName,
20
+ command: renderAgentClientInvocation(adapter.invocation, {
21
+ projectName: input.projectName,
22
+ skillName: input.skillName,
23
+ args: input.taskRef === undefined ? [] : [input.taskRef]
24
+ })
25
+ }));
26
+ const custom = input.customTUIs.map((tool) => Object.freeze({
27
+ source: 'custom',
28
+ displayName: tool.name,
29
+ command: renderAgentClientInvocation(tool.invocation, {
30
+ projectName: input.projectName,
31
+ skillName: input.skillName,
32
+ args: input.taskRef === undefined ? [] : [input.taskRef]
33
+ })
34
+ }));
35
+ return Object.freeze([...builtins, ...custom]);
36
+ }
37
+ export { renderNextStepCommands };
38
+ //# sourceMappingURL=next-steps.js.map
@@ -0,0 +1,45 @@
1
+ function unique(values) {
2
+ return Object.freeze([...new Set(values)]);
3
+ }
4
+ function planAgentClientProjectAssets(input) {
5
+ const enabledIds = new Set(input.enabledAdapters.map((adapter) => adapter.id));
6
+ const enabledAdapters = input.allAdapters.filter((adapter) => enabledIds.has(adapter.id));
7
+ const disabledAdapters = input.allAdapters.filter((adapter) => !enabledIds.has(adapter.id));
8
+ const enabledManaged = unique(enabledAdapters.flatMap((adapter) => adapter.project.managed));
9
+ const enabledMerged = unique(enabledAdapters.flatMap((adapter) => adapter.project.merged));
10
+ const enabledEjected = unique(enabledAdapters.flatMap((adapter) => adapter.project.ejected));
11
+ const disabledManaged = unique(disabledAdapters.flatMap((adapter) => adapter.project.managed));
12
+ const allAdapterAssets = new Set(input.allAdapters.flatMap((adapter) => [
13
+ ...adapter.project.managed,
14
+ ...adapter.project.merged,
15
+ ...adapter.project.ejected
16
+ ]));
17
+ const ejected = unique([
18
+ ...input.current.ejected,
19
+ ...enabledEjected,
20
+ ...input.sharedDefaults.ejected
21
+ ]);
22
+ const ejectedSet = new Set(ejected);
23
+ const preserveCurrent = (values, enabledValues) => values.filter((entry) => !ejectedSet.has(entry)
24
+ && (!allAdapterAssets.has(entry) || enabledValues.includes(entry)));
25
+ const managed = unique([
26
+ ...preserveCurrent(input.current.managed, enabledManaged),
27
+ ...enabledManaged.filter((entry) => !ejectedSet.has(entry)),
28
+ ...input.sharedDefaults.managed.filter((entry) => !ejectedSet.has(entry))
29
+ ]);
30
+ const managedSet = new Set(managed);
31
+ const merged = unique([
32
+ ...preserveCurrent(input.current.merged, enabledMerged),
33
+ ...enabledMerged.filter((entry) => !ejectedSet.has(entry) && !managedSet.has(entry)),
34
+ ...input.sharedDefaults.merged.filter((entry) => !ejectedSet.has(entry) && !managedSet.has(entry))
35
+ ]);
36
+ return Object.freeze({
37
+ registry: Object.freeze({ managed, merged, ejected }),
38
+ enabledManaged,
39
+ enabledMerged,
40
+ enabledEjected,
41
+ disabledManaged
42
+ });
43
+ }
44
+ export { planAgentClientProjectAssets };
45
+ //# sourceMappingURL=project-assets.js.map
@@ -0,0 +1,235 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import defaults from '../defaults.json' with { type: 'json' };
5
+ import { normalizeAgentClients, serializeAgentClients } from './config.js';
6
+ import { normalizeCustomTUIs } from './custom-tuis.js';
7
+ import { renderNextStepCommands } from './next-steps.js';
8
+ import { planAgentClientProjectAssets } from './project-assets.js';
9
+ import { listAgentClientAdapters, listEnabledAgentClientAdapters } from './registry.js';
10
+ function isRecord(value) {
11
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
12
+ }
13
+ function hash(content) {
14
+ return `sha256:${crypto.createHash('sha256').update(content).digest('hex')}`;
15
+ }
16
+ function trustedBaseline(value) {
17
+ return typeof value === 'string' && /^sha256:[a-f0-9]{64}$/.test(value)
18
+ ? value
19
+ : null;
20
+ }
21
+ function cloneState(state) {
22
+ return Object.fromEntries(Object.entries(state).map(([id, value]) => [id, { ...value }]));
23
+ }
24
+ function applyMutation(state, mutation) {
25
+ if (mutation.type === 'none')
26
+ return cloneState(state);
27
+ if (mutation.type === 'replace-state')
28
+ return cloneState(mutation.state);
29
+ return Object.fromEntries(Object.entries(state).map(([id, value]) => [
30
+ id,
31
+ id === mutation.id ? { ...value, enabled: mutation.value } : { ...value }
32
+ ]));
33
+ }
34
+ function registryFrom(value) {
35
+ const files = isRecord(value) ? value : {};
36
+ const strings = (candidate) => Array.isArray(candidate)
37
+ ? candidate.filter((entry) => typeof entry === 'string')
38
+ : [];
39
+ return {
40
+ managed: strings(files.managed),
41
+ merged: strings(files.merged),
42
+ ejected: strings(files.ejected)
43
+ };
44
+ }
45
+ function isPathOwnedByOtherPlatform(relativePath, platformType) {
46
+ const top = relativePath.replace(/\\/g, '/').replace(/^\.\//, '').split('/')[0] ?? '';
47
+ return top.startsWith('.')
48
+ && ['github', 'none'].includes(top.slice(1))
49
+ && top.slice(1) !== platformType;
50
+ }
51
+ function renderSeed(content, config) {
52
+ return content
53
+ .replaceAll('{{project}}', String(config.project ?? ''))
54
+ .replaceAll('{{org}}', String(config.org ?? ''));
55
+ }
56
+ function planAgentClientReconciliation(input) {
57
+ const normalized = normalizeAgentClients(input.config);
58
+ const desired = applyMutation(normalized.state, input.mutation);
59
+ const adapters = listAgentClientAdapters();
60
+ const current = registryFrom(input.config.files);
61
+ const sharedDefaults = {
62
+ managed: defaults.files.managed.filter((entry) => !isPathOwnedByOtherPlatform(entry, input.platformType)),
63
+ merged: defaults.files.merged.filter((entry) => !isPathOwnedByOtherPlatform(entry, input.platformType)),
64
+ ejected: defaults.files.ejected
65
+ };
66
+ const projectAssets = planAgentClientProjectAssets({
67
+ current,
68
+ sharedDefaults,
69
+ enabledAdapters: listEnabledAgentClientAdapters(desired),
70
+ allAdapters: adapters
71
+ });
72
+ const nextConfig = structuredClone(input.config);
73
+ nextConfig.agentClients = serializeAgentClients(desired);
74
+ delete nextConfig.tuis;
75
+ if (normalized.remainingSandboxTools !== undefined) {
76
+ const sandbox = isRecord(nextConfig.sandbox) ? { ...nextConfig.sandbox } : {};
77
+ sandbox.tools = [
78
+ 'agent-infra',
79
+ ...normalized.remainingSandboxTools.filter((tool) => tool !== 'agent-infra')
80
+ ];
81
+ nextConfig.sandbox = sandbox;
82
+ }
83
+ const existingFiles = isRecord(nextConfig.files) ? nextConfig.files : {};
84
+ const existingBaselines = isRecord(existingFiles.managedBaselines)
85
+ ? existingFiles.managedBaselines
86
+ : {};
87
+ const baselines = { ...existingBaselines };
88
+ nextConfig.files = {
89
+ ...existingFiles,
90
+ managed: [...projectAssets.registry.managed],
91
+ merged: [...projectAssets.registry.merged],
92
+ ejected: [...projectAssets.registry.ejected],
93
+ managedBaselines: baselines
94
+ };
95
+ const projectName = String(input.config.project ?? '');
96
+ const language = input.language === 'zh-CN' ? 'zh-CN' : 'en';
97
+ const seedOperations = [];
98
+ for (const adapter of adapters) {
99
+ for (const seed of adapter.project.seedCommands) {
100
+ const relativeTarget = seed.target.replaceAll('${projectName}', projectName);
101
+ const target = path.join(input.projectRoot, relativeTarget);
102
+ const templatePath = path.join(input.templateRoot, seed.templates[language]);
103
+ const content = renderSeed(fs.readFileSync(templatePath, 'utf8'), input.config);
104
+ const templateHash = hash(content);
105
+ const exists = fs.existsSync(target);
106
+ const localHash = exists ? hash(fs.readFileSync(target)) : null;
107
+ const baseline = trustedBaseline(baselines[relativeTarget]);
108
+ const enabled = desired[adapter.id].enabled;
109
+ if (enabled) {
110
+ if (!exists || (baseline !== null && localHash === baseline)) {
111
+ baselines[relativeTarget] = templateHash;
112
+ seedOperations.push({
113
+ kind: localHash === templateHash ? 'unchanged' : 'write',
114
+ clientId: adapter.id,
115
+ target: relativeTarget,
116
+ ...(localHash === templateHash ? {} : { content })
117
+ });
118
+ }
119
+ else if (localHash === templateHash) {
120
+ baselines[relativeTarget] = templateHash;
121
+ seedOperations.push({ kind: 'unchanged', clientId: adapter.id, target: relativeTarget });
122
+ }
123
+ else {
124
+ seedOperations.push({
125
+ kind: 'protect',
126
+ clientId: adapter.id,
127
+ target: relativeTarget,
128
+ reason: baseline ? 'user-modified' : 'unknown-origin'
129
+ });
130
+ }
131
+ }
132
+ else if (!exists) {
133
+ delete baselines[relativeTarget];
134
+ seedOperations.push({ kind: 'unchanged', clientId: adapter.id, target: relativeTarget });
135
+ }
136
+ else if ((baseline !== null && localHash === baseline) || localHash === templateHash) {
137
+ delete baselines[relativeTarget];
138
+ seedOperations.push({ kind: 'remove', clientId: adapter.id, target: relativeTarget });
139
+ }
140
+ else {
141
+ seedOperations.push({
142
+ kind: 'protect',
143
+ clientId: adapter.id,
144
+ target: relativeTarget,
145
+ reason: baseline ? 'user-modified' : 'unknown-origin'
146
+ });
147
+ }
148
+ }
149
+ }
150
+ if (Object.keys(baselines).length === 0) {
151
+ delete nextConfig.files.managedBaselines;
152
+ }
153
+ const custom = normalizeCustomTUIs(input.projectRoot, input.config.customTUIs ?? []);
154
+ const nextSteps = renderNextStepCommands({
155
+ projectName,
156
+ state: desired,
157
+ customTUIs: custom.items,
158
+ skillName: 'update-agent-infra'
159
+ });
160
+ const actionableSeed = seedOperations.some((operation) => operation.kind === 'write' || operation.kind === 'remove');
161
+ const changed = JSON.stringify(nextConfig) !== JSON.stringify(input.config) || actionableSeed;
162
+ return Object.freeze({
163
+ source: normalized.source,
164
+ before: normalized.state,
165
+ desired,
166
+ nextConfig,
167
+ projectAssets,
168
+ seedOperations: Object.freeze(seedOperations),
169
+ nextSteps,
170
+ diagnostics: Object.freeze([...normalized.diagnostics, ...custom.diagnostics]),
171
+ changed,
172
+ projectRoot: input.projectRoot,
173
+ configPath: path.join(input.projectRoot, '.agents', '.airc.json')
174
+ });
175
+ }
176
+ function applyAgentClientReconciliation(plan, dependencies = {}) {
177
+ const mkdirSync = dependencies.mkdirSync ?? fs.mkdirSync;
178
+ const writeFileSync = dependencies.writeFileSync ?? fs.writeFileSync;
179
+ const renameSync = dependencies.renameSync ?? fs.renameSync;
180
+ const unlinkSync = dependencies.unlinkSync ?? fs.unlinkSync;
181
+ const applied = [];
182
+ const protectedTargets = [];
183
+ const unchanged = [];
184
+ for (const operation of plan.seedOperations) {
185
+ const target = path.join(plan.projectRoot, operation.target);
186
+ if (operation.kind === 'write') {
187
+ mkdirSync(path.dirname(target), { recursive: true });
188
+ writeFileSync(target, operation.content, 'utf8');
189
+ applied.push(operation.target);
190
+ }
191
+ else if (operation.kind === 'remove') {
192
+ if (fs.existsSync(target))
193
+ unlinkSync(target);
194
+ applied.push(operation.target);
195
+ }
196
+ else if (operation.kind === 'protect') {
197
+ protectedTargets.push(operation.target);
198
+ }
199
+ else {
200
+ unchanged.push(operation.target);
201
+ }
202
+ }
203
+ const serialized = `${JSON.stringify(plan.nextConfig, null, 2)}\n`;
204
+ const previous = fs.existsSync(plan.configPath)
205
+ ? fs.readFileSync(plan.configPath, 'utf8')
206
+ : null;
207
+ const configUpdated = previous !== serialized;
208
+ if (configUpdated) {
209
+ mkdirSync(path.dirname(plan.configPath), { recursive: true });
210
+ const temporary = `${plan.configPath}.tmp-${process.pid}-${Date.now()}`;
211
+ try {
212
+ writeFileSync(temporary, serialized, 'utf8');
213
+ renameSync(temporary, plan.configPath);
214
+ }
215
+ catch (error) {
216
+ try {
217
+ if (fs.existsSync(temporary))
218
+ unlinkSync(temporary);
219
+ }
220
+ catch {
221
+ // Preserve the original error; a later idempotent rerun can clean stale state.
222
+ }
223
+ throw error;
224
+ }
225
+ }
226
+ return Object.freeze({
227
+ status: configUpdated || applied.length > 0 ? 'applied' : 'unchanged',
228
+ configUpdated,
229
+ applied: Object.freeze(applied),
230
+ protected: Object.freeze(protectedTargets),
231
+ unchanged: Object.freeze(unchanged)
232
+ });
233
+ }
234
+ export { applyAgentClientReconciliation, planAgentClientReconciliation };
235
+ //# sourceMappingURL=reconcile.js.map
@@ -0,0 +1,72 @@
1
+ import { BUILTIN_AGENT_CLIENT_ADAPTERS } from './adapters/index.js';
2
+ import { AGENT_CLIENT_IDS, isAgentClientId } from './types.js';
3
+ function buildRegistry() {
4
+ if (BUILTIN_AGENT_CLIENT_ADAPTERS.length !== AGENT_CLIENT_IDS.length) {
5
+ throw new Error('Agent Client Registry is incomplete');
6
+ }
7
+ const byId = new Map(BUILTIN_AGENT_CLIENT_ADAPTERS.map((adapter) => [adapter.id, adapter]));
8
+ if (byId.size !== BUILTIN_AGENT_CLIENT_ADAPTERS.length) {
9
+ throw new Error('Agent Client Registry contains duplicate IDs');
10
+ }
11
+ const adapters = Object.freeze(AGENT_CLIENT_IDS.map((id) => {
12
+ const adapter = byId.get(id);
13
+ if (!adapter || adapter.id !== id) {
14
+ throw new Error(`Agent Client Registry is missing '${id}'`);
15
+ }
16
+ return adapter;
17
+ }));
18
+ const ownedPaths = adapters.flatMap((adapter) => adapter.project.ownedPathPrefixes.map((prefix) => ({
19
+ id: adapter.id,
20
+ prefix
21
+ })));
22
+ for (const [index, current] of ownedPaths.entries()) {
23
+ for (const other of ownedPaths.slice(index + 1)) {
24
+ if (current.id !== other.id
25
+ && (current.prefix.startsWith(other.prefix)
26
+ || other.prefix.startsWith(current.prefix))) {
27
+ throw new Error(`Agent Client owned paths overlap: '${current.prefix}' and '${other.prefix}'`);
28
+ }
29
+ }
30
+ }
31
+ const registry = Object.freeze(Object.fromEntries(adapters.map((adapter) => [adapter.id, adapter])));
32
+ return Object.freeze({ registry, adapters });
33
+ }
34
+ const built = buildRegistry();
35
+ const AGENT_CLIENT_REGISTRY = built.registry;
36
+ const AGENT_CLIENT_ADAPTERS = built.adapters;
37
+ function listAgentClientAdapters() {
38
+ return AGENT_CLIENT_ADAPTERS;
39
+ }
40
+ function getAgentClientAdapter(id) {
41
+ if (!isAgentClientId(id)) {
42
+ throw new Error(`No Agent Client adapter registered for '${String(id)}'`);
43
+ }
44
+ return AGENT_CLIENT_REGISTRY[id];
45
+ }
46
+ function listEnabledAgentClientAdapters(state) {
47
+ return Object.freeze(AGENT_CLIENT_ADAPTERS.filter((adapter) => state[adapter.id].enabled));
48
+ }
49
+ function listInstalledAgentClientAdapters(state) {
50
+ return Object.freeze(AGENT_CLIENT_ADAPTERS.filter((adapter) => state[adapter.id].installInSandbox));
51
+ }
52
+ function getAgentClientCapability(id, capability) {
53
+ return getAgentClientAdapter(id).capabilities[capability];
54
+ }
55
+ function listEnabledAgentClientAdaptersByCapability(state, capability, acceptedLevels) {
56
+ const accepted = new Set(acceptedLevels);
57
+ return Object.freeze(AGENT_CLIENT_ADAPTERS.filter((adapter) => state[adapter.id].enabled
58
+ && accepted.has(adapter.capabilities[capability].level)));
59
+ }
60
+ function createAgentClientManifest() {
61
+ return Object.freeze(AGENT_CLIENT_ADAPTERS.map((adapter) => Object.freeze({
62
+ id: adapter.id,
63
+ displayName: adapter.displayName,
64
+ invocation: adapter.invocation,
65
+ ownedPathPrefixes: Object.freeze([...adapter.project.ownedPathPrefixes]),
66
+ managed: Object.freeze([...adapter.project.managed]),
67
+ merged: Object.freeze([...adapter.project.merged]),
68
+ ejected: Object.freeze([...adapter.project.ejected])
69
+ })));
70
+ }
71
+ export { AGENT_CLIENT_REGISTRY, createAgentClientManifest, getAgentClientAdapter, getAgentClientCapability, listAgentClientAdapters, listEnabledAgentClientAdapters, listEnabledAgentClientAdaptersByCapability, listInstalledAgentClientAdapters };
72
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1,57 @@
1
+ import { AGENT_CLIENT_CAPABILITY_IDS, AGENT_CLIENT_IDS, AGENT_CLIENT_SUPPORT_LEVELS } from './types.js';
2
+ const AGENT_CLIENTS_SCHEMA = {
3
+ $schema: 'http://json-schema.org/draft-07/schema#',
4
+ type: 'object',
5
+ required: ['agentClients'],
6
+ properties: {
7
+ agentClients: {
8
+ type: 'array',
9
+ minItems: AGENT_CLIENT_IDS.length,
10
+ maxItems: AGENT_CLIENT_IDS.length,
11
+ uniqueItems: true,
12
+ description: 'Canonical Agent Client configuration. Object ID completeness and uniqueness are enforced by the runtime normalizer.',
13
+ items: {
14
+ type: 'object',
15
+ additionalProperties: false,
16
+ required: ['id', 'enabled', 'installInSandbox'],
17
+ properties: {
18
+ id: {
19
+ type: 'string',
20
+ enum: [...AGENT_CLIENT_IDS]
21
+ },
22
+ enabled: {
23
+ type: 'boolean'
24
+ },
25
+ installInSandbox: {
26
+ type: 'boolean'
27
+ }
28
+ }
29
+ }
30
+ },
31
+ customTUIs: {
32
+ type: 'array',
33
+ items: {
34
+ type: 'object',
35
+ additionalProperties: true,
36
+ required: ['name', 'dir', 'invoke'],
37
+ properties: {
38
+ name: { type: 'string' },
39
+ dir: { type: 'string' },
40
+ invoke: { type: 'string' }
41
+ }
42
+ }
43
+ }
44
+ },
45
+ definitions: {
46
+ agentClientCapabilityId: {
47
+ type: 'string',
48
+ enum: [...AGENT_CLIENT_CAPABILITY_IDS]
49
+ },
50
+ agentClientSupportLevel: {
51
+ type: 'string',
52
+ enum: [...AGENT_CLIENT_SUPPORT_LEVELS]
53
+ }
54
+ }
55
+ };
56
+ export { AGENT_CLIENTS_SCHEMA };
57
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1,29 @@
1
+ const AGENT_CLIENT_IDS = [
2
+ 'claude-code',
3
+ 'codex',
4
+ 'gemini-cli',
5
+ 'opencode'
6
+ ];
7
+ const AGENT_CLIENT_CAPABILITY_IDS = [
8
+ 'instructions',
9
+ 'skills',
10
+ 'commands',
11
+ 'hooks',
12
+ 'subagents',
13
+ 'orchestration',
14
+ 'sandbox',
15
+ 'verification'
16
+ ];
17
+ const AGENT_CLIENT_SUPPORT_LEVELS = [
18
+ 'compatible',
19
+ 'integrated',
20
+ 'verified',
21
+ 'experimental',
22
+ 'unsupported'
23
+ ];
24
+ function isAgentClientId(value) {
25
+ return typeof value === 'string'
26
+ && AGENT_CLIENT_IDS.includes(value);
27
+ }
28
+ export { AGENT_CLIENT_IDS, AGENT_CLIENT_CAPABILITY_IDS, AGENT_CLIENT_SUPPORT_LEVELS, isAgentClientId };
29
+ //# sourceMappingURL=types.js.map
@@ -1,18 +1,13 @@
1
- const BUILTIN_TUI_IDS = ['claude-code', 'codex', 'gemini-cli', 'opencode'];
2
- const BUILTIN_TUI_DISPLAY = {
3
- 'claude-code': 'Claude Code',
4
- 'codex': 'Codex',
5
- 'gemini-cli': 'Gemini CLI',
6
- 'opencode': 'OpenCode'
7
- };
8
- const BUILTIN_TUI_OWNED_PATH_PREFIXES = {
9
- 'claude-code': ['.claude/'],
10
- 'codex': ['.codex/'],
11
- 'gemini-cli': ['.gemini/'],
12
- 'opencode': ['.opencode/']
13
- };
1
+ import { AGENT_CLIENT_IDS, isAgentClientId } from './agent-clients/types.js';
2
+ import { listAgentClientAdapters } from './agent-clients/registry.js';
3
+ const BUILTIN_TUI_IDS = AGENT_CLIENT_IDS;
4
+ const BUILTIN_TUI_DISPLAY = Object.fromEntries(listAgentClientAdapters().map((adapter) => [adapter.id, adapter.displayName]));
5
+ const BUILTIN_TUI_OWNED_PATH_PREFIXES = Object.fromEntries(listAgentClientAdapters().map((adapter) => [
6
+ adapter.id,
7
+ [...adapter.project.ownedPathPrefixes]
8
+ ]));
14
9
  function isBuiltinTUIId(value) {
15
- return typeof value === 'string' && BUILTIN_TUI_IDS.includes(value);
10
+ return isAgentClientId(value);
16
11
  }
17
12
  function resolveEnabledTUIs(value) {
18
13
  // Missing field / null / non-array → full set (backward compat for legacy