@aifabrix/builder 2.54.1 → 2.55.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 (253) hide show
  1. package/README.md +8 -0
  2. package/lib/agent-kit/apply-ops.js +82 -0
  3. package/lib/agent-kit/checksums.js +28 -0
  4. package/lib/agent-kit/classify.js +113 -0
  5. package/lib/agent-kit/claude-shim.js +61 -0
  6. package/lib/agent-kit/constants.js +55 -0
  7. package/lib/agent-kit/file-list.js +103 -0
  8. package/lib/agent-kit/host-approve.js +172 -0
  9. package/lib/agent-kit/hosting/github.js +63 -0
  10. package/lib/agent-kit/hosts.js +71 -0
  11. package/lib/agent-kit/init.js +319 -0
  12. package/lib/agent-kit/kit-root.js +17 -0
  13. package/lib/agent-kit/managed-section.js +79 -0
  14. package/lib/agent-kit/package-load.js +45 -0
  15. package/lib/agent-kit/plan-ops.js +69 -0
  16. package/lib/agent-kit/remote-prompts.js +272 -0
  17. package/lib/agent-kit/render-host-markdown.js +40 -0
  18. package/lib/agent-kit/repo-guard.js +47 -0
  19. package/lib/agent-kit/report.js +107 -0
  20. package/lib/agent-kit/run.js +82 -0
  21. package/lib/agent-kit/scm/git.js +122 -0
  22. package/lib/agent-kit/start.js +310 -0
  23. package/lib/agent-kit/state-file.js +83 -0
  24. package/lib/agent-kit/status.js +50 -0
  25. package/lib/agent-kit/v1-detect.js +81 -0
  26. package/lib/agent-kit/validate-kit.js +87 -0
  27. package/lib/agent-kit/workspace-name.js +40 -0
  28. package/lib/agent-kit/workspace-skeleton.js +103 -0
  29. package/lib/api/role-assistant-test-pack.api.js +86 -0
  30. package/lib/api/work-search.api.js +31 -4
  31. package/lib/api/work.api.js +19 -12
  32. package/lib/app/deploy.js +7 -4
  33. package/lib/app/list-external-systems.js +2 -1
  34. package/lib/app/offer-create-from-template.js +184 -0
  35. package/lib/app/show-display.js +33 -6
  36. package/lib/app/show.js +25 -2
  37. package/lib/channels/add-channel.js +1 -0
  38. package/lib/channels/approval-guides/claude.js +2 -2
  39. package/lib/channels/channel-add-failure.js +1 -0
  40. package/lib/channels/channel-add-remaining.js +15 -9
  41. package/lib/channels/channel-definition-status.js +115 -0
  42. package/lib/channels/channel-description.js +2 -1
  43. package/lib/channels/channel-install-envelope.js +4 -2
  44. package/lib/channels/channel-integration-generator.js +2 -0
  45. package/lib/channels/channel-runtime-resolver.js +14 -0
  46. package/lib/channels/channel-targets.js +31 -5
  47. package/lib/channels/claude-setup-values.js +4 -1
  48. package/lib/channels/slack-channel-add.js +59 -6
  49. package/lib/channels/slack-mcp-callback.js +51 -0
  50. package/lib/channels/slack-mcp-runtime.js +156 -0
  51. package/lib/channels/slack-provisioning/install-cli.js +14 -2
  52. package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
  53. package/lib/cli/index.js +4 -0
  54. package/lib/cli/setup-app.js +8 -1
  55. package/lib/cli/setup-auth.js +11 -2
  56. package/lib/cli/setup-certification-commands.js +2 -2
  57. package/lib/cli/setup-certification.help.js +3 -1
  58. package/lib/commands/agent-kit-help.js +149 -0
  59. package/lib/commands/agent-kit.js +417 -0
  60. package/lib/commands/auth-status-user.js +99 -0
  61. package/lib/commands/auth-status.js +14 -4
  62. package/lib/commands/channel-add-help.js +5 -5
  63. package/lib/commands/datasource-capability-relate-cli.js +5 -4
  64. package/lib/commands/datasource-load-export-cli.js +7 -2
  65. package/lib/commands/datasource-test-trust-cli.js +5 -1
  66. package/lib/commands/governance-command-action.js +5 -4
  67. package/lib/commands/identity-membership-role-cache.js +6 -1
  68. package/lib/commands/lifecycle-command-action.js +16 -15
  69. package/lib/commands/login-credentials.js +8 -3
  70. package/lib/commands/login-device.js +14 -5
  71. package/lib/commands/login.js +14 -1
  72. package/lib/commands/logout.js +50 -15
  73. package/lib/commands/repair-auth-apply.js +1 -1
  74. package/lib/commands/repair-datasource-key-skip.js +39 -0
  75. package/lib/commands/repair-datasource-keys.js +24 -12
  76. package/lib/commands/repair-rbac.js +16 -10
  77. package/lib/commands/role-assistant-copy-output.js +61 -0
  78. package/lib/commands/role-assistant-copy.js +155 -0
  79. package/lib/commands/role-assistant-test-output.js +8 -9
  80. package/lib/commands/role-assistant-test-result-rows.js +42 -0
  81. package/lib/commands/role-assistant.js +140 -59
  82. package/lib/commands/search.js +143 -0
  83. package/lib/commands/test-trust-external.js +7 -5
  84. package/lib/commands/upload-sidecar-followups.js +4 -1
  85. package/lib/commands/verify-governance-command-action.js +9 -7
  86. package/lib/commands/verify-operations-command-action.js +9 -8
  87. package/lib/commands/verify-operations-skip-e2e.js +131 -0
  88. package/lib/commands/verify-operations-step-plan.js +6 -33
  89. package/lib/commands/verify-operations-steps.js +3 -1
  90. package/lib/commands/verify-trust-command-action.js +6 -5
  91. package/lib/datasource/capability/relate-operations.js +28 -10
  92. package/lib/datasource/capability/run-capability-relate.js +48 -25
  93. package/lib/datasource/datasource-identifier.js +13 -5
  94. package/lib/deployment/deployer-poll-run.js +117 -35
  95. package/lib/external-system/deploy-dataplane-published.js +70 -0
  96. package/lib/external-system/deploy.js +12 -2
  97. package/lib/external-system/download-role-assistant.js +41 -15
  98. package/lib/external-system/download.js +8 -3
  99. package/lib/generator/helpers.js +12 -1
  100. package/lib/generator/wizard-datasource-key.js +54 -0
  101. package/lib/generator/wizard.js +55 -54
  102. package/lib/integration-definition/apply-datasource-keys.js +75 -0
  103. package/lib/integration-definition/apply-relationships.js +195 -0
  104. package/lib/integration-definition/apply-static-enterprise.js +232 -0
  105. package/lib/integration-definition/apply-system-file.js +71 -0
  106. package/lib/integration-definition/apply-test-endpoint.js +89 -0
  107. package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
  108. package/lib/integration-definition/apply.js +78 -59
  109. package/lib/integration-definition/enrich-openapi-operations.js +36 -10
  110. package/lib/integration-definition/enrich-openapi-pick.js +87 -0
  111. package/lib/lifecycle/role-assistant-governance.js +56 -21
  112. package/lib/programmatic/capability-registry.js +1 -0
  113. package/lib/programmatic/index.js +2 -0
  114. package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
  115. package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
  116. package/lib/programmatic/openapi-component-schemas.js +1 -0
  117. package/lib/programmatic/openapi-descriptions.js +9 -3
  118. package/lib/programmatic/openapi-envelope-examples.js +7 -0
  119. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  120. package/lib/programmatic/openapi-request-schemas.js +5 -0
  121. package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
  122. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  123. package/lib/programmatic/openapi-spec.js +1 -0
  124. package/lib/programmatic/request-timeout.js +3 -0
  125. package/lib/programmatic/route-handler-map.js +7 -0
  126. package/lib/programmatic/run-channel-catalog.js +50 -8
  127. package/lib/programmatic/run-channel-install-options.js +5 -1
  128. package/lib/programmatic/run-download.js +41 -0
  129. package/lib/protection/auth-context.js +3 -1
  130. package/lib/protection/protection-display.js +3 -2
  131. package/lib/protection/report-exit.js +50 -0
  132. package/lib/protection/run-commands.js +2 -0
  133. package/lib/role-assistant/knowledge-sync-settings-push.js +172 -0
  134. package/lib/role-assistant/knowledge-sync.js +9 -126
  135. package/lib/role-assistant/package-validate.js +175 -0
  136. package/lib/role-assistant/test-case-assertion-row.js +36 -0
  137. package/lib/role-assistant/test-case-copy-bind.js +77 -0
  138. package/lib/role-assistant/test-case-copy-expected.js +127 -0
  139. package/lib/role-assistant/test-case-copy-from-work.js +90 -0
  140. package/lib/role-assistant/test-case-copy-interactions.js +67 -0
  141. package/lib/role-assistant/test-case-copy.js +119 -0
  142. package/lib/role-assistant/test-case-expected-catalog.js +135 -0
  143. package/lib/role-assistant/test-case-validate.js +88 -0
  144. package/lib/role-assistant/test-cases-bid.js +117 -4
  145. package/lib/role-assistant/test-cases-evaluate.js +149 -31
  146. package/lib/role-assistant/test-cases-evidence.js +44 -5
  147. package/lib/role-assistant/test-cases-honesty.js +155 -6
  148. package/lib/role-assistant/test-cases-lifecycle.js +297 -0
  149. package/lib/role-assistant/test-cases-search-graph.js +122 -0
  150. package/lib/role-assistant/test-cases-search.js +20 -2
  151. package/lib/role-assistant/test-cases.js +34 -168
  152. package/lib/role-assistant/test-pack-map.js +180 -0
  153. package/lib/role-assistant/test-pack-sync.js +187 -0
  154. package/lib/role-assistant/test-runner-debug.js +54 -0
  155. package/lib/role-assistant/test-runner-respond-body.js +36 -0
  156. package/lib/role-assistant/test-runner-search.js +20 -5
  157. package/lib/role-assistant/test-runner-start-payload.js +63 -10
  158. package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
  159. package/lib/role-assistant/test-runner-workhub.js +45 -21
  160. package/lib/role-assistant/test-runner.js +53 -10
  161. package/lib/schema/external-datasource.schema.json +36 -5
  162. package/lib/schema/external-system.schema.json +20 -2
  163. package/lib/schema/infra-parameter.schema.json +51 -3
  164. package/lib/schema/infra.parameter.yaml +476 -40
  165. package/lib/schema/role-assistant-test-case.schema.json +408 -0
  166. package/lib/schema/type/document-storage.json +1 -1
  167. package/lib/search/dataplane-context.js +23 -0
  168. package/lib/search/search-args.js +211 -0
  169. package/lib/search/search-display.js +238 -0
  170. package/lib/search/search-errors.js +50 -0
  171. package/lib/search/search-prompts.js +119 -0
  172. package/lib/search/search-roles.js +85 -0
  173. package/lib/search/search-run.js +241 -0
  174. package/lib/search/search-skills.js +198 -0
  175. package/lib/utils/api-required-permissions.js +271 -0
  176. package/lib/utils/api.js +16 -13
  177. package/lib/utils/authentication-slot-show.js +153 -0
  178. package/lib/utils/cli-json-exit.js +41 -0
  179. package/lib/utils/cli-utils.js +23 -5
  180. package/lib/utils/controller-url.js +78 -4
  181. package/lib/utils/credential-display.js +43 -1
  182. package/lib/utils/deployment-errors.js +15 -0
  183. package/lib/utils/deployment-validation-helpers.js +4 -1
  184. package/lib/utils/device-code.js +31 -10
  185. package/lib/utils/error-formatters/error-parser.js +16 -1
  186. package/lib/utils/error-formatters/permission-errors.js +19 -5
  187. package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
  188. package/lib/utils/external-system-readiness-compact-display.js +30 -1
  189. package/lib/utils/external-system-validators.js +2 -1
  190. package/lib/utils/help-builder.js +3 -1
  191. package/lib/utils/keycloak-realm-ready.js +5 -2
  192. package/lib/utils/logger.js +1 -0
  193. package/lib/utils/paths.js +26 -5
  194. package/lib/utils/schema-loader.js +37 -0
  195. package/lib/utils/secrets-helpers.js +2 -2
  196. package/lib/utils/write-stream-and-wait.js +63 -0
  197. package/lib/validation/validate-external-file.js +5 -1
  198. package/lib/validation/validate.js +3 -2
  199. package/package.json +20 -6
  200. package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
  201. package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
  202. package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
  203. package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
  204. package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
  205. package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
  206. package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
  207. package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
  208. package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
  209. package/templates/agent-kit/agent-kit.yaml +45 -0
  210. package/templates/agent-kit/instructions/AGENTKIT.md +104 -0
  211. package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
  212. package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
  213. package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
  214. package/templates/agent-kit/parity-inventory.yaml +188 -0
  215. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +59 -0
  216. package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
  217. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
  218. package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
  219. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
  220. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +52 -0
  221. package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
  222. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
  223. package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
  224. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +51 -0
  225. package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
  226. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
  227. package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
  228. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +43 -0
  229. package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
  230. package/templates/agent-kit/skills/shared/hosts.md +65 -0
  231. package/templates/agent-kit/skills/shared/login.md +16 -0
  232. package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
  233. package/templates/agent-kit/workspace/LICENSE.mit +21 -0
  234. package/templates/agent-kit/workspace/README.md +89 -0
  235. package/templates/applications/builder-api/Dockerfile +3 -1
  236. package/templates/applications/dataplane/application.yaml +2 -3
  237. package/templates/applications/dataplane/env.template +5 -5
  238. package/templates/applications/dataplane/rbac.yaml +28 -0
  239. package/templates/applications/keycloak/env.template +3 -3
  240. package/templates/applications/miso-controller/application.yaml +21 -2
  241. package/templates/applications/miso-controller/env.template +96 -25
  242. package/templates/applications/miso-controller/rbac.yaml +5 -0
  243. package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
  244. package/templates/channels/claude/channel.json +1 -1
  245. package/templates/channels/claude/install.template.md.hbs +1 -0
  246. package/templates/channels/claude/integration/claude-system.json +1 -1
  247. package/templates/channels/slack/channel.json +11 -0
  248. package/templates/channels/slack/install.template.md.hbs +9 -0
  249. package/templates/channels/slack/integration/slack-system.json +11 -0
  250. package/templates/channels/slack/manifest.template.json.hbs +6 -3
  251. package/templates/channels/teams-bot/channel.json +1 -0
  252. package/templates/python/docker-compose.hbs +14 -0
  253. package/templates/typescript/docker-compose.hbs +14 -0
@@ -0,0 +1,272 @@
1
+ /**
2
+ * Interactive prompts for agent-kit init and remote create.
3
+ * @fileoverview TTY host, org, visibility, and confirm; injectable for tests
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const { execFileSync } = require('child_process');
9
+ const { ALL_HOSTS, HOST_LABELS, normalizeHosts } = require('./hosts');
10
+ const { APPROVE_HOSTS, parseApproveAll } = require('./host-approve');
11
+
12
+ /**
13
+ * @param {{ execFileSync?: Function }} [options]
14
+ * @returns {string}
15
+ */
16
+ function detectGithubLogin(options = {}) {
17
+ const exec = options.execFileSync || execFileSync;
18
+ try {
19
+ const out = exec('gh', ['api', 'user', '-q', '.login'], {
20
+ encoding: 'utf8',
21
+ stdio: ['ignore', 'pipe', 'pipe']
22
+ });
23
+ return String(out || '').trim();
24
+ } catch {
25
+ return '';
26
+ }
27
+ }
28
+
29
+ /**
30
+ * @param {string[]} argv
31
+ * @param {string} flag
32
+ * @returns {boolean}
33
+ */
34
+ function argvHasFlag(argv, flag) {
35
+ return argv.includes(flag) || argv.some((a) => a.startsWith(`${flag}=`));
36
+ }
37
+
38
+ /**
39
+ * Ask which AI coding hosts to install for (TTY only when --host not set).
40
+ * @param {object} options
41
+ * @param {{ interactive?: boolean, inquirer?: object, argv?: string[] }} [deps]
42
+ * @returns {Promise<object>}
43
+ */
44
+ async function enrichInitHostOptions(options, deps = {}) {
45
+ const interactive = Boolean(deps.interactive);
46
+ const argv = deps.argv || process.argv;
47
+ if (!interactive || argvHasFlag(argv, '--host')) {
48
+ return options;
49
+ }
50
+ const inquirer = deps.inquirer || require('inquirer');
51
+ const { host } = await inquirer.prompt([
52
+ {
53
+ type: 'list',
54
+ name: 'host',
55
+ message: 'Which AI coding platforms should Agent Kit support?',
56
+ default: 'all',
57
+ choices: [
58
+ {
59
+ name: `All (${ALL_HOSTS.map((h) => HOST_LABELS[h] || h).join(', ')})`,
60
+ value: 'all'
61
+ },
62
+ ...ALL_HOSTS.map((h) => ({
63
+ name: HOST_LABELS[h] || h,
64
+ value: h
65
+ }))
66
+ ]
67
+ }
68
+ ]);
69
+ return { ...options, host: host || 'all' };
70
+ }
71
+
72
+ /**
73
+ * @param {object} deps
74
+ * @returns {Promise<import('inquirer').Answers>}
75
+ */
76
+ async function askRemoteFields(deps) {
77
+ const inquirer = deps.inquirer || require('inquirer');
78
+ const defaultOrg = deps.defaultOrg || '';
79
+ return inquirer.prompt([
80
+ {
81
+ type: 'input',
82
+ name: 'org',
83
+ message: 'GitHub org or user:',
84
+ default: defaultOrg || undefined,
85
+ when: () => !deps.org,
86
+ validate: (v) => (String(v || '').trim() ? true : 'Org or user is required')
87
+ },
88
+ {
89
+ type: 'list',
90
+ name: 'visibility',
91
+ message: 'Repository visibility:',
92
+ choices: [
93
+ { name: 'private (recommended)', value: 'private' },
94
+ { name: 'public', value: 'public' }
95
+ ],
96
+ default: 'private',
97
+ when: () => !deps.visibilitySet
98
+ },
99
+ {
100
+ type: 'confirm',
101
+ name: 'confirmed',
102
+ message: (answers) => {
103
+ const org = deps.org || answers.org;
104
+ const vis = deps.visibility || answers.visibility || 'private';
105
+ const full = `${org}/${deps.name}`;
106
+ return `Create github:${full} (${vis}) and push?`;
107
+ },
108
+ default: false,
109
+ when: () => !deps.yes
110
+ }
111
+ ]);
112
+ }
113
+
114
+ /**
115
+ * Fill org/visibility and confirm on TTY. Non-interactive requires --org.
116
+ * @param {object} options
117
+ * @param {{ interactive?: boolean, name?: string, inquirer?: object, execFileSync?: Function, argv?: string[] }} [deps]
118
+ * @returns {Promise<object>}
119
+ */
120
+ async function enrichRemoteOptions(options, deps = {}) {
121
+ const interactive = Boolean(deps.interactive);
122
+ const name = deps.name || options.name || 'repo';
123
+ const argv = deps.argv || process.argv;
124
+ const visibilitySet = argvHasFlag(argv, '--visibility');
125
+ let org = options.org;
126
+ let visibility = options.visibility || 'private';
127
+
128
+ if (!interactive) {
129
+ if (!org) {
130
+ throw new Error('Non-interactive remote create requires --org <owner>.');
131
+ }
132
+ return { ...options, org, visibility, name };
133
+ }
134
+
135
+ const defaultOrg = org || detectGithubLogin({ execFileSync: deps.execFileSync });
136
+ const answers = await askRemoteFields({
137
+ inquirer: deps.inquirer,
138
+ org,
139
+ name,
140
+ visibility,
141
+ visibilitySet,
142
+ defaultOrg,
143
+ yes: Boolean(options.yes)
144
+ });
145
+ org = String(org || answers.org || '').trim();
146
+ if (answers.visibility) {
147
+ visibility = answers.visibility;
148
+ }
149
+ if (!org) {
150
+ throw new Error('Org or user is required.');
151
+ }
152
+ if (options.yes !== true && answers.confirmed === false) {
153
+ return { ...options, org, visibility, name, cancelled: true };
154
+ }
155
+ return { ...options, org, visibility, name, remote: true };
156
+ }
157
+
158
+ /**
159
+ * On TTY without --remote/--no-remote, ask whether to create a remote.
160
+ * @param {object} options
161
+ * @param {{ interactive?: boolean, inquirer?: object, execFileSync?: Function, workspaceName?: string }} [deps]
162
+ * @returns {Promise<object>}
163
+ */
164
+ async function enrichInitRemoteOptions(options, deps = {}) {
165
+ const interactive = Boolean(deps.interactive);
166
+ const remoteExplicit = options.remote === true;
167
+ const noRemote = options.noRemote === true || options.remote === false;
168
+
169
+ if (noRemote || !interactive) {
170
+ return options;
171
+ }
172
+
173
+ let wantRemote = remoteExplicit;
174
+ if (!remoteExplicit) {
175
+ const inquirer = deps.inquirer || require('inquirer');
176
+ const { createRemote } = await inquirer.prompt([
177
+ {
178
+ type: 'confirm',
179
+ name: 'createRemote',
180
+ message: 'Create a private GitHub remote and push now?',
181
+ default: false
182
+ }
183
+ ]);
184
+ wantRemote = Boolean(createRemote);
185
+ }
186
+ if (!wantRemote) {
187
+ return { ...options, remote: false };
188
+ }
189
+
190
+ return enrichRemoteOptions(
191
+ { ...options, remote: true },
192
+ {
193
+ interactive: true,
194
+ name: deps.workspaceName || options.nameOrPath,
195
+ inquirer: deps.inquirer,
196
+ execFileSync: deps.execFileSync
197
+ }
198
+ );
199
+ }
200
+
201
+ /**
202
+ * @param {object} inquirer
203
+ * @param {string[]} eligible
204
+ * @returns {Promise<true|false|string>}
205
+ */
206
+ async function askApproveAllChoice(inquirer, eligible) {
207
+ if (eligible.length === 1) {
208
+ const host = eligible[0];
209
+ const { approveAll } = await inquirer.prompt([
210
+ {
211
+ type: 'confirm',
212
+ name: 'approveAll',
213
+ message: `Auto-approve ${HOST_LABELS[host] || host} tool permissions in this workspace?`,
214
+ default: true
215
+ }
216
+ ]);
217
+ return Boolean(approveAll);
218
+ }
219
+ const { approveAll } = await inquirer.prompt([
220
+ {
221
+ type: 'list',
222
+ name: 'approveAll',
223
+ message: 'Which coding host should auto-approve tools in this workspace?',
224
+ default: 'all',
225
+ choices: [
226
+ { name: 'Both Claude Code and Codex', value: 'all' },
227
+ { name: 'Claude Code only', value: 'claude' },
228
+ { name: 'Codex only', value: 'codex' },
229
+ { name: 'Skip (the coding app will ask each time)', value: 'skip' }
230
+ ]
231
+ }
232
+ ]);
233
+ return approveAll === 'skip' ? false : approveAll;
234
+ }
235
+
236
+ /**
237
+ * TTY asks; --approve-all [host] / --yes / non-TTY auto-approve; --skip-approve-all skips.
238
+ * @param {object} options
239
+ * @param {{ interactive?: boolean, inquirer?: object, ask?: boolean }} [deps]
240
+ * @returns {Promise<object>}
241
+ */
242
+ async function enrichApproveAllOptions(options, deps = {}) {
243
+ const hosts = normalizeHosts(options.host);
244
+ const eligible = hosts.filter((h) => APPROVE_HOSTS.includes(h));
245
+ if (!eligible.length || options.skipApproveAll) {
246
+ return { ...options, approveAll: false };
247
+ }
248
+ if (options.approveAll) {
249
+ parseApproveAll(options.approveAll);
250
+ return options;
251
+ }
252
+ if (options.yes) {
253
+ return { ...options, approveAll: true };
254
+ }
255
+ if (deps.ask === false) {
256
+ return { ...options, approveAll: false };
257
+ }
258
+ if (!deps.interactive) {
259
+ return { ...options, approveAll: true };
260
+ }
261
+ const inquirer = deps.inquirer || require('inquirer');
262
+ const approveAll = await askApproveAllChoice(inquirer, eligible);
263
+ return { ...options, approveAll };
264
+ }
265
+
266
+ module.exports = {
267
+ detectGithubLogin,
268
+ enrichInitHostOptions,
269
+ enrichRemoteOptions,
270
+ enrichInitRemoteOptions,
271
+ enrichApproveAllOptions
272
+ };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Render Agent Kit markdown for the selected coding hosts.
3
+ * @fileoverview Handlebars host flags for AGENTKIT.md
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const Handlebars = require('handlebars');
9
+ const { ALL_HOSTS } = require('./hosts');
10
+
11
+ /**
12
+ * @param {string[]} hosts
13
+ * @returns {object}
14
+ */
15
+ function hostMarkdownContext(hosts) {
16
+ const selected = Array.isArray(hosts) && hosts.length ? hosts : ALL_HOSTS.slice();
17
+ const cursor = selected.includes('cursor');
18
+ const claude = selected.includes('claude');
19
+ const codex = selected.includes('codex');
20
+ const hostFlag = selected.length === ALL_HOSTS.length ? 'all' : selected.join(',');
21
+ return {
22
+ cursor,
23
+ claude,
24
+ codex,
25
+ autoApprove: claude || codex,
26
+ hostFlag
27
+ };
28
+ }
29
+
30
+ /**
31
+ * @param {string} source
32
+ * @param {string[]} hosts
33
+ * @returns {string}
34
+ */
35
+ function renderHostMarkdown(source, hosts) {
36
+ const template = Handlebars.compile(String(source || ''), { noEscape: true });
37
+ return template(hostMarkdownContext(hosts)).replace(/\n{3,}/g, '\n\n');
38
+ }
39
+
40
+ module.exports = { hostMarkdownContext, renderHostMarkdown };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Git repository root guard for Agent Kit install.
3
+ * @fileoverview Refuse home, filesystem root, and non-git directories
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const os = require('os');
9
+ const path = require('path');
10
+ const { execFileSync } = require('child_process');
11
+
12
+ /**
13
+ * @param {string} resolved
14
+ * @returns {boolean}
15
+ */
16
+ function isUnsafeRoot(resolved) {
17
+ const home = path.resolve(os.homedir());
18
+ const root = path.resolve(resolved);
19
+ if (root === path.parse(root).root) {
20
+ return true;
21
+ }
22
+ return root === home;
23
+ }
24
+
25
+ /**
26
+ * @param {string} cwd
27
+ * @returns {string}
28
+ */
29
+ function resolveGitRoot(cwd) {
30
+ let output;
31
+ try {
32
+ output = execFileSync('git', ['rev-parse', '--show-toplevel'], {
33
+ cwd,
34
+ encoding: 'utf8',
35
+ stdio: ['ignore', 'pipe', 'pipe']
36
+ }).trim();
37
+ } catch {
38
+ throw new Error('Agent Kit commands must run inside a git repository.');
39
+ }
40
+ const resolved = path.resolve(output);
41
+ if (isUnsafeRoot(resolved)) {
42
+ throw new Error('Refusing to install the Agent Kit into a filesystem root or home directory.');
43
+ }
44
+ return resolved;
45
+ }
46
+
47
+ module.exports = { isUnsafeRoot, resolveGitRoot };
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Human and JSON reports for Agent Kit commands.
3
+ * @fileoverview Status envelope without ANSI in JSON
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const { ACTION, KIT_STATE } = require('./constants');
9
+ const { hostLabel } = require('./hosts');
10
+
11
+ /**
12
+ * @param {object[]} ops
13
+ * @returns {string[]}
14
+ */
15
+ function conflictPaths(ops) {
16
+ return ops.filter((op) => op.action === ACTION.CONFLICT).map((op) => op.targetRel);
17
+ }
18
+
19
+ /**
20
+ * @param {object[]} ops
21
+ * @returns {number}
22
+ */
23
+ function countInstalledSkills(ops) {
24
+ return (ops || []).filter((op) => {
25
+ const rel = op.targetRel || '';
26
+ const skillFile = rel.includes('.agents/skills/') && rel.endsWith('SKILL.md');
27
+ return skillFile && op.action !== ACTION.DELETE && op.action !== ACTION.CONFLICT;
28
+ }).length;
29
+ }
30
+
31
+ /**
32
+ * @param {object|null} state
33
+ * @param {object} kit
34
+ * @param {object[]} ops
35
+ * @returns {string}
36
+ */
37
+ function kitStateName(state, kit, ops) {
38
+ if (!state) {
39
+ return KIT_STATE.NOT_INSTALLED;
40
+ }
41
+ if (conflictPaths(ops).length > 0) {
42
+ return KIT_STATE.CONFLICTS;
43
+ }
44
+ if (state.kitVersion !== kit.manifest.kitVersion) {
45
+ return KIT_STATE.UPDATE_AVAILABLE;
46
+ }
47
+ const applied = new Set([ACTION.CREATE, ACTION.REPLACE, ACTION.APPEND_SECTION]);
48
+ const missing = ops.some((op) => {
49
+ return op.fileState === 'MISSING' && op.mode !== 'obsolete' && !applied.has(op.action);
50
+ });
51
+ return missing ? KIT_STATE.UPDATE_AVAILABLE : KIT_STATE.CURRENT;
52
+ }
53
+
54
+ /**
55
+ * @param {object} params
56
+ * @returns {object}
57
+ */
58
+ function buildJsonReport(params) {
59
+ const { command, kit, state, hosts, ops, dryRun, builderVersion, kitState, approvals } = params;
60
+ return {
61
+ success: conflictPaths(ops).length === 0,
62
+ command,
63
+ dryRun: Boolean(dryRun),
64
+ state: kitState,
65
+ kitVersion: kit.manifest.kitVersion,
66
+ installedKitVersion: state ? state.kitVersion : null,
67
+ builderVersion,
68
+ skills: {
69
+ expected: (kit.manifest.skills || []).length,
70
+ installed: countInstalledSkills(ops)
71
+ },
72
+ hosts,
73
+ files: (ops || []).map((op) => ({
74
+ path: op.targetRel,
75
+ classification: op.fileState,
76
+ action: op.action
77
+ })),
78
+ conflicts: conflictPaths(ops),
79
+ approvals: approvals || { requested: false, files: [] }
80
+ };
81
+ }
82
+
83
+ /**
84
+ * @param {object} json
85
+ * @returns {string}
86
+ */
87
+ function formatHumanStatus(json) {
88
+ const installed = json.installedKitVersion || '—';
89
+ const hosts = (json.hosts || []).map((h) => hostLabel(h)).join(', ');
90
+ const lines = [
91
+ 'AI Fabrix Agent Kit',
92
+ `Installed ${installed}`,
93
+ `Available ${json.kitVersion} (Builder ${json.builderVersion})`,
94
+ `Skills ${json.skills.installed}/${json.skills.expected}`,
95
+ `Hosts ${hosts || '—'}`,
96
+ `State ${json.state}`
97
+ ];
98
+ if (json.approvals && json.approvals.requested) {
99
+ const paths = (json.approvals.files || [])
100
+ .filter((row) => row.action !== 'skip')
101
+ .map((row) => `${row.path} (${row.action})`);
102
+ lines.push(`Approvals ${paths.join(', ') || 'none'}`);
103
+ }
104
+ return lines.join('\n');
105
+ }
106
+
107
+ module.exports = { conflictPaths, kitStateName, buildJsonReport, formatHumanStatus };
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Shared Agent Kit install/update runner.
3
+ * @fileoverview Classify, plan, optionally apply, write state
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const { getCliVersion } = require('../utils/installation-log-core');
9
+ const { resolveGitRoot } = require('./repo-guard');
10
+ const { loadKitPackage } = require('./package-load');
11
+ const { listManagedTargets } = require('./file-list');
12
+ const { classifyItems } = require('./classify');
13
+ const { detectV1, listObsoleteV1Items, markV1ShimReplacements } = require('./v1-detect');
14
+ const { loadState, buildStateDocument, writeState } = require('./state-file');
15
+ const { actionForInstall, actionForUpdate, assignActions } = require('./plan-ops');
16
+ const { applyOps } = require('./apply-ops');
17
+ const { resolveManagedHosts } = require('./hosts');
18
+ const { kitStateName, buildJsonReport } = require('./report');
19
+ const { writeApproveAllFiles } = require('./host-approve');
20
+
21
+ /**
22
+ * @param {object} options
23
+ * @param {'install'|'update'} mode
24
+ * @returns {object}
25
+ */
26
+ function runManagedCommand(options, mode) {
27
+ const repoRoot = resolveGitRoot(options.cwd || process.cwd());
28
+ const kit = loadKitPackage();
29
+ const state = loadState(repoRoot);
30
+ const hosts = resolveManagedHosts(options, state, mode);
31
+ const includeShims = detectV1(repoRoot) && hosts.includes('cursor');
32
+ const includeClaude = hosts.includes('claude');
33
+ const items = listManagedTargets(kit, repoRoot, { includeShims, includeClaude });
34
+ classifyItems(items, state, hosts);
35
+ markV1ShimReplacements(items, kit);
36
+ const managedRels = new Set(items.map((item) => item.targetRel));
37
+ items.push(...listObsoleteV1Items(kit, repoRoot, managedRels));
38
+ const picker = mode === 'update' ? actionForUpdate : actionForInstall;
39
+ const ops = assignActions(items, picker, Boolean(options.force));
40
+ if (!options.dryRun) {
41
+ applyOps(ops);
42
+ writeState(repoRoot, buildStateDocument(kit, hosts, ops, getCliVersion()));
43
+ }
44
+ const builderVersion = getCliVersion();
45
+ const kitState = kitStateName(options.dryRun ? state : loadState(repoRoot) || state, kit, ops);
46
+ const approvals = writeApproveAllFiles({
47
+ repoRoot,
48
+ hosts,
49
+ approveAll: options.approveAll,
50
+ dryRun: Boolean(options.dryRun),
51
+ force: Boolean(options.force)
52
+ });
53
+ return buildJsonReport({
54
+ command: mode,
55
+ kit,
56
+ state: options.dryRun ? state : loadState(repoRoot) || state,
57
+ hosts,
58
+ ops,
59
+ dryRun: Boolean(options.dryRun),
60
+ builderVersion,
61
+ kitState,
62
+ approvals
63
+ });
64
+ }
65
+
66
+ /**
67
+ * @param {object} options
68
+ * @returns {object}
69
+ */
70
+ function runInstall(options) {
71
+ return runManagedCommand(options, 'install');
72
+ }
73
+
74
+ /**
75
+ * @param {object} options
76
+ * @returns {object}
77
+ */
78
+ function runUpdate(options) {
79
+ return runManagedCommand(options, 'update');
80
+ }
81
+
82
+ module.exports = { runInstall, runUpdate, runManagedCommand };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Local git SCM helpers for agent-kit init.
3
+ * @fileoverview git init / detect / initial commit
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const path = require('path');
9
+ const fs = require('../../internal/fs-real-sync');
10
+ const { execFileSync } = require('child_process');
11
+
12
+ const INITIAL_COMMIT_MESSAGE = 'Initial customer workspace with Agent Kit.';
13
+
14
+ /**
15
+ * @param {string} dir
16
+ * @returns {boolean}
17
+ */
18
+ function isGitRepository(dir) {
19
+ return fs.existsSync(path.join(dir, '.git'));
20
+ }
21
+
22
+ /**
23
+ * @param {string} dir
24
+ * @param {{ dryRun?: boolean, execFileSync?: Function }} [options]
25
+ * @returns {{ initialized: boolean, skipped: boolean }}
26
+ */
27
+ function ensureGitRepository(dir, options = {}) {
28
+ const exec = options.execFileSync || execFileSync;
29
+ if (isGitRepository(dir)) {
30
+ return { initialized: false, skipped: true };
31
+ }
32
+ if (options.dryRun) {
33
+ return { initialized: true, skipped: false };
34
+ }
35
+ exec('git', ['init', '-b', 'main'], { cwd: dir, stdio: 'ignore' });
36
+ return { initialized: true, skipped: false };
37
+ }
38
+
39
+ /**
40
+ * @param {string} dir
41
+ * @param {Function} exec
42
+ * @returns {boolean}
43
+ */
44
+ function hasCommits(dir, exec) {
45
+ try {
46
+ exec('git', ['rev-parse', 'HEAD'], { cwd: dir, stdio: 'ignore' });
47
+ return true;
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Create the first commit when HEAD is missing (required before gh --push).
55
+ * Uses one-shot -c identity; does not rewrite git config.
56
+ * @param {string} dir
57
+ * @param {{ dryRun?: boolean, execFileSync?: Function }} [options]
58
+ * @returns {{ committed: boolean, skipped: boolean, planned?: boolean }}
59
+ */
60
+ function ensureInitialCommit(dir, options = {}) {
61
+ const exec = options.execFileSync || execFileSync;
62
+ if (options.dryRun) {
63
+ return { committed: true, skipped: false, planned: true };
64
+ }
65
+ if (hasCommits(dir, exec)) {
66
+ return { committed: false, skipped: true };
67
+ }
68
+ exec('git', ['add', '-A'], { cwd: dir, stdio: 'ignore' });
69
+ const status = exec('git', ['status', '--porcelain'], {
70
+ cwd: dir,
71
+ encoding: 'utf8',
72
+ stdio: ['ignore', 'pipe', 'pipe']
73
+ });
74
+ if (!String(status || '').trim()) {
75
+ throw new Error('No files to commit before creating the remote.');
76
+ }
77
+ exec(
78
+ 'git',
79
+ [
80
+ '-c',
81
+ 'user.email=noreply@aifabrix.ai',
82
+ '-c',
83
+ 'user.name=AI Fabrix Builder',
84
+ 'commit',
85
+ '-m',
86
+ INITIAL_COMMIT_MESSAGE
87
+ ],
88
+ { cwd: dir, stdio: 'ignore' }
89
+ );
90
+ return { committed: true, skipped: false };
91
+ }
92
+
93
+ /**
94
+ * @param {string} dir
95
+ * @param {{ execFileSync?: Function }} [options]
96
+ * @returns {boolean}
97
+ */
98
+ function hasOriginRemote(dir, options = {}) {
99
+ const exec = options.execFileSync || execFileSync;
100
+ try {
101
+ const out = exec('git', ['remote'], {
102
+ cwd: dir,
103
+ encoding: 'utf8',
104
+ stdio: ['ignore', 'pipe', 'pipe']
105
+ });
106
+ return String(out)
107
+ .split(/\r?\n/)
108
+ .map((line) => line.trim())
109
+ .includes('origin');
110
+ } catch {
111
+ return false;
112
+ }
113
+ }
114
+
115
+ module.exports = {
116
+ isGitRepository,
117
+ ensureGitRepository,
118
+ ensureInitialCommit,
119
+ hasOriginRemote,
120
+ hasCommits,
121
+ INITIAL_COMMIT_MESSAGE
122
+ };