@aifabrix/builder 2.54.1 → 2.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) 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 +130 -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 +239 -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/state-file.js +83 -0
  23. package/lib/agent-kit/status.js +50 -0
  24. package/lib/agent-kit/v1-detect.js +81 -0
  25. package/lib/agent-kit/validate-kit.js +87 -0
  26. package/lib/agent-kit/workspace-name.js +40 -0
  27. package/lib/agent-kit/workspace-skeleton.js +103 -0
  28. package/lib/api/role-assistant-test-pack.api.js +86 -0
  29. package/lib/api/work-search.api.js +31 -4
  30. package/lib/api/work.api.js +19 -12
  31. package/lib/app/list-external-systems.js +2 -1
  32. package/lib/app/show-display.js +33 -6
  33. package/lib/app/show.js +25 -2
  34. package/lib/channels/add-channel.js +1 -0
  35. package/lib/channels/approval-guides/claude.js +2 -2
  36. package/lib/channels/channel-add-failure.js +1 -0
  37. package/lib/channels/channel-add-remaining.js +15 -9
  38. package/lib/channels/channel-definition-status.js +115 -0
  39. package/lib/channels/channel-description.js +2 -1
  40. package/lib/channels/channel-install-envelope.js +4 -2
  41. package/lib/channels/channel-integration-generator.js +2 -0
  42. package/lib/channels/channel-runtime-resolver.js +14 -0
  43. package/lib/channels/channel-targets.js +31 -5
  44. package/lib/channels/claude-setup-values.js +4 -1
  45. package/lib/channels/slack-channel-add.js +59 -6
  46. package/lib/channels/slack-mcp-callback.js +51 -0
  47. package/lib/channels/slack-mcp-runtime.js +156 -0
  48. package/lib/channels/slack-provisioning/install-cli.js +14 -2
  49. package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
  50. package/lib/cli/index.js +4 -0
  51. package/lib/cli/setup-certification-commands.js +2 -2
  52. package/lib/cli/setup-certification.help.js +3 -1
  53. package/lib/commands/agent-kit-help.js +79 -0
  54. package/lib/commands/agent-kit.js +346 -0
  55. package/lib/commands/channel-add-help.js +5 -5
  56. package/lib/commands/datasource-capability-relate-cli.js +5 -4
  57. package/lib/commands/repair-auth-apply.js +1 -1
  58. package/lib/commands/repair-datasource-key-skip.js +39 -0
  59. package/lib/commands/repair-datasource-keys.js +24 -12
  60. package/lib/commands/repair-rbac.js +16 -10
  61. package/lib/commands/role-assistant-copy-output.js +61 -0
  62. package/lib/commands/role-assistant-copy.js +155 -0
  63. package/lib/commands/role-assistant-test-output.js +8 -9
  64. package/lib/commands/role-assistant-test-result-rows.js +42 -0
  65. package/lib/commands/role-assistant.js +117 -48
  66. package/lib/commands/search.js +143 -0
  67. package/lib/commands/test-trust-external.js +7 -5
  68. package/lib/commands/upload-sidecar-followups.js +4 -1
  69. package/lib/commands/verify-operations-skip-e2e.js +131 -0
  70. package/lib/commands/verify-operations-step-plan.js +6 -33
  71. package/lib/commands/verify-operations-steps.js +3 -1
  72. package/lib/datasource/capability/relate-operations.js +28 -10
  73. package/lib/datasource/capability/run-capability-relate.js +48 -25
  74. package/lib/datasource/datasource-identifier.js +13 -5
  75. package/lib/deployment/deployer-poll-run.js +117 -35
  76. package/lib/external-system/deploy-dataplane-published.js +70 -0
  77. package/lib/external-system/deploy.js +12 -2
  78. package/lib/external-system/download-role-assistant.js +41 -15
  79. package/lib/external-system/download.js +7 -1
  80. package/lib/generator/wizard-datasource-key.js +54 -0
  81. package/lib/generator/wizard.js +55 -54
  82. package/lib/integration-definition/apply-datasource-keys.js +75 -0
  83. package/lib/integration-definition/apply-relationships.js +195 -0
  84. package/lib/integration-definition/apply-static-enterprise.js +229 -0
  85. package/lib/integration-definition/apply-system-file.js +71 -0
  86. package/lib/integration-definition/apply-test-endpoint.js +89 -0
  87. package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
  88. package/lib/integration-definition/apply.js +78 -59
  89. package/lib/integration-definition/enrich-openapi-operations.js +36 -10
  90. package/lib/integration-definition/enrich-openapi-pick.js +87 -0
  91. package/lib/lifecycle/role-assistant-governance.js +56 -21
  92. package/lib/programmatic/capability-registry.js +1 -0
  93. package/lib/programmatic/index.js +2 -0
  94. package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
  95. package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
  96. package/lib/programmatic/openapi-component-schemas.js +1 -0
  97. package/lib/programmatic/openapi-descriptions.js +9 -3
  98. package/lib/programmatic/openapi-envelope-examples.js +7 -0
  99. package/lib/programmatic/openapi-operation-bindings.js +5 -0
  100. package/lib/programmatic/openapi-request-schemas.js +5 -0
  101. package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
  102. package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
  103. package/lib/programmatic/openapi-spec.js +1 -0
  104. package/lib/programmatic/request-timeout.js +3 -0
  105. package/lib/programmatic/route-handler-map.js +7 -0
  106. package/lib/programmatic/run-channel-catalog.js +50 -8
  107. package/lib/programmatic/run-channel-install-options.js +5 -1
  108. package/lib/programmatic/run-download.js +41 -0
  109. package/lib/protection/auth-context.js +3 -1
  110. package/lib/protection/protection-display.js +3 -2
  111. package/lib/protection/report-exit.js +50 -0
  112. package/lib/protection/run-commands.js +2 -0
  113. package/lib/role-assistant/package-validate.js +175 -0
  114. package/lib/role-assistant/test-case-assertion-row.js +36 -0
  115. package/lib/role-assistant/test-case-copy-bind.js +77 -0
  116. package/lib/role-assistant/test-case-copy-expected.js +127 -0
  117. package/lib/role-assistant/test-case-copy-from-work.js +90 -0
  118. package/lib/role-assistant/test-case-copy-interactions.js +67 -0
  119. package/lib/role-assistant/test-case-copy.js +119 -0
  120. package/lib/role-assistant/test-case-expected-catalog.js +135 -0
  121. package/lib/role-assistant/test-case-validate.js +88 -0
  122. package/lib/role-assistant/test-cases-bid.js +117 -4
  123. package/lib/role-assistant/test-cases-evaluate.js +137 -30
  124. package/lib/role-assistant/test-cases-honesty.js +155 -6
  125. package/lib/role-assistant/test-cases-lifecycle.js +291 -0
  126. package/lib/role-assistant/test-cases-search-graph.js +122 -0
  127. package/lib/role-assistant/test-cases-search.js +20 -2
  128. package/lib/role-assistant/test-cases.js +34 -168
  129. package/lib/role-assistant/test-pack-map.js +180 -0
  130. package/lib/role-assistant/test-pack-sync.js +187 -0
  131. package/lib/role-assistant/test-runner-debug.js +54 -0
  132. package/lib/role-assistant/test-runner-respond-body.js +36 -0
  133. package/lib/role-assistant/test-runner-search.js +20 -5
  134. package/lib/role-assistant/test-runner-start-payload.js +63 -10
  135. package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
  136. package/lib/role-assistant/test-runner-workhub.js +45 -21
  137. package/lib/role-assistant/test-runner.js +50 -10
  138. package/lib/schema/external-datasource.schema.json +36 -5
  139. package/lib/schema/external-system.schema.json +20 -2
  140. package/lib/schema/role-assistant-test-case.schema.json +408 -0
  141. package/lib/search/dataplane-context.js +23 -0
  142. package/lib/search/search-args.js +211 -0
  143. package/lib/search/search-display.js +238 -0
  144. package/lib/search/search-errors.js +50 -0
  145. package/lib/search/search-prompts.js +119 -0
  146. package/lib/search/search-roles.js +85 -0
  147. package/lib/search/search-run.js +241 -0
  148. package/lib/search/search-skills.js +198 -0
  149. package/lib/utils/authentication-slot-show.js +153 -0
  150. package/lib/utils/credential-display.js +43 -1
  151. package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
  152. package/lib/utils/external-system-readiness-compact-display.js +30 -1
  153. package/lib/utils/external-system-validators.js +2 -1
  154. package/lib/utils/help-builder.js +3 -1
  155. package/lib/utils/schema-loader.js +37 -0
  156. package/lib/validation/validate-external-file.js +5 -1
  157. package/lib/validation/validate.js +3 -2
  158. package/package.json +1 -1
  159. package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
  160. package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
  161. package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
  162. package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
  163. package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
  164. package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
  165. package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
  166. package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
  167. package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
  168. package/templates/agent-kit/agent-kit.yaml +45 -0
  169. package/templates/agent-kit/instructions/AGENTKIT.md +83 -0
  170. package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
  171. package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
  172. package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
  173. package/templates/agent-kit/parity-inventory.yaml +188 -0
  174. package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +58 -0
  175. package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
  176. package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
  177. package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
  178. package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
  179. package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +51 -0
  180. package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
  181. package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
  182. package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
  183. package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +50 -0
  184. package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
  185. package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
  186. package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
  187. package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +42 -0
  188. package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
  189. package/templates/agent-kit/skills/shared/hosts.md +78 -0
  190. package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
  191. package/templates/agent-kit/workspace/LICENSE.mit +21 -0
  192. package/templates/agent-kit/workspace/README.md +89 -0
  193. package/templates/applications/builder-api/Dockerfile +3 -1
  194. package/templates/applications/dataplane/application.yaml +2 -2
  195. package/templates/applications/dataplane/env.template +1 -1
  196. package/templates/applications/dataplane/rbac.yaml +28 -0
  197. package/templates/applications/keycloak/env.template +3 -3
  198. package/templates/applications/miso-controller/application.yaml +1 -1
  199. package/templates/applications/miso-controller/env.template +8 -9
  200. package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
  201. package/templates/channels/claude/channel.json +1 -1
  202. package/templates/channels/claude/install.template.md.hbs +1 -0
  203. package/templates/channels/claude/integration/claude-system.json +1 -1
  204. package/templates/channels/slack/channel.json +11 -0
  205. package/templates/channels/slack/install.template.md.hbs +9 -0
  206. package/templates/channels/slack/integration/slack-system.json +11 -0
  207. package/templates/channels/slack/manifest.template.json.hbs +6 -3
  208. package/templates/channels/teams-bot/channel.json +1 -0
  209. package/templates/python/docker-compose.hbs +14 -0
  210. package/templates/typescript/docker-compose.hbs +14 -0
@@ -0,0 +1,346 @@
1
+ /**
2
+ * `aifabrix agent-kit` command group.
3
+ * @fileoverview Install, init, status, update, validate, remote create
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const logger = require('../utils/logger');
9
+ const { handleCommandError } = require('../utils/cli-utils');
10
+ const { formatSuccessLine, formatBlockingError, formatWarningLine } = require('../utils/cli-layout-chalk');
11
+ const { runInstall, runUpdate } = require('../agent-kit/run');
12
+ const path = require('path');
13
+ const { runInit, runRemoteCreate } = require('../agent-kit/init');
14
+ const { enrichInitHostOptions, enrichInitRemoteOptions, enrichRemoteOptions, enrichApproveAllOptions } = require('../agent-kit/remote-prompts');
15
+ const { hostLabel } = require('../agent-kit/hosts');
16
+ const { runStatus } = require('../agent-kit/status');
17
+ const { runValidate } = require('../agent-kit/validate-kit');
18
+ const { formatHumanStatus } = require('../agent-kit/report');
19
+ const { KIT_STATE } = require('../agent-kit/constants');
20
+ const { loadState } = require('../agent-kit/state-file');
21
+ const { resolveGitRoot } = require('../agent-kit/repo-guard');
22
+ const {
23
+ GROUP_HELP,
24
+ INSTALL_HELP,
25
+ STATUS_HELP,
26
+ UPDATE_HELP,
27
+ VALIDATE_HELP,
28
+ INIT_HELP,
29
+ REMOTE_HELP
30
+ } = require('./agent-kit-help');
31
+
32
+ /**
33
+ * @param {object} json
34
+ * @param {boolean} asJson
35
+ * @returns {void}
36
+ */
37
+ function emitReport(json, asJson) {
38
+ if (asJson) {
39
+ logger.log(JSON.stringify(json, null, 2));
40
+ return;
41
+ }
42
+ logger.log(formatHumanStatus(json));
43
+ if (json.conflicts && json.conflicts.length) {
44
+ logger.log(formatWarningLine(`Conflicts: ${json.conflicts.join(', ')}`));
45
+ }
46
+ }
47
+
48
+ /**
49
+ * @param {object} json
50
+ * @param {boolean} asJson
51
+ * @returns {void}
52
+ */
53
+ function emitInitReport(json, asJson) {
54
+ if (asJson) {
55
+ logger.log(JSON.stringify(json, null, 2));
56
+ return;
57
+ }
58
+ logger.log('AI Fabrix workspace init');
59
+ logger.log(`Path ${json.path}`);
60
+ logger.log(`Name ${json.name}`);
61
+ logger.log(`SCM ${json.scm}`);
62
+ if (json.hosts && json.hosts.length) {
63
+ logger.log(`Hosts ${json.hosts.map((h) => hostLabel(h)).join(', ')}`);
64
+ }
65
+ if (json.kit) {
66
+ logger.log(`Agent Kit ${json.kit.kitVersion} (${json.kit.state})`);
67
+ }
68
+ if (json.remote && !json.remote.skipped) {
69
+ logger.log(`Remote ${json.remote.provider}:${json.remote.fullName} (${json.remote.visibility})`);
70
+ } else {
71
+ logger.log('Remote skipped (use: aifabrix agent-kit remote create)');
72
+ }
73
+ logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Done'));
74
+ }
75
+
76
+ /**
77
+ * @param {object} json
78
+ * @param {boolean} asJson
79
+ * @returns {void}
80
+ */
81
+ function emitRemoteReport(json, asJson) {
82
+ if (asJson) {
83
+ logger.log(JSON.stringify(json, null, 2));
84
+ return;
85
+ }
86
+ if (json.remote && json.remote.skipped) {
87
+ logger.log(formatWarningLine(`Remote skipped: ${json.remote.reason || 'already configured'}`));
88
+ } else if (json.remote) {
89
+ logger.log(`Remote ${json.remote.provider}:${json.remote.fullName} (${json.remote.visibility})`);
90
+ }
91
+ logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Done'));
92
+ }
93
+
94
+ /**
95
+ * @param {Function} runner
96
+ * @param {object} options
97
+ * @param {boolean} [failOnConflict]
98
+ * @returns {Promise<void>}
99
+ */
100
+ async function runLeaf(runner, options, failOnConflict) {
101
+ const json = runner(options);
102
+ emitReport(json, Boolean(options.json));
103
+ if (failOnConflict && json.conflicts && json.conflicts.length) {
104
+ logger.error(formatBlockingError('Managed files were modified locally. Re-run with --force to replace kit-owned content only.'));
105
+ process.exitCode = 1;
106
+ return;
107
+ }
108
+ if (!options.json && json.state && json.state !== KIT_STATE.NOT_INSTALLED) {
109
+ logger.log(formatSuccessLine(json.dryRun ? 'Dry-run complete' : 'Done'));
110
+ }
111
+ }
112
+
113
+ /**
114
+ * @param {import('commander').Command} program
115
+ */
116
+ function setupAgentKitCommands(program) {
117
+ const group = program
118
+ .command('agent-kit')
119
+ .description('Install, init, and update the AI Fabrix Agent Kit in a customer workspace')
120
+ .addHelpText('after', GROUP_HELP);
121
+
122
+ addInitCommand(group);
123
+ addInstallCommand(group);
124
+ addStatusCommand(group);
125
+ addUpdateCommand(group);
126
+ addValidateCommand(group);
127
+ addRemoteCommand(group);
128
+ }
129
+
130
+ /**
131
+ * @param {import('commander').Command} group
132
+ */
133
+ function addInitCommand(group) {
134
+ group
135
+ .command('init [nameOrPath]')
136
+ .description('Create a customer workspace skeleton, git repo, and Agent Kit')
137
+ .option('--host <target>', 'all, cursor, codex, or claude (omit on TTY to choose)', 'all')
138
+ .option('--scm <kind>', 'git or none', 'git')
139
+ .option('--remote', 'Create private hosting remote and push (requires auth)')
140
+ .option('--no-remote', 'Force local-only (skip remote prompt on TTY)')
141
+ .option('--org <owner>', 'Hosting org or user (required with --remote when non-TTY)')
142
+ .option('--visibility <mode>', 'private or public', 'private')
143
+ .option('--license <kind>', 'mit or none', 'none')
144
+ .option('--force', 'Allow non-empty target directory')
145
+ .option('--yes', 'Silent: skip TTY confirm (pair with --host and --org); also auto-approves host permissions')
146
+ .option('--approve-all', 'Write Claude Code and Codex auto-approve configs')
147
+ .option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
148
+ .option('--dry-run', 'Plan writes without changing files')
149
+ .option('--json', 'Machine-readable output')
150
+ .addHelpText('after', INIT_HELP)
151
+ .action(async(nameOrPath, options) => {
152
+ try {
153
+ const interactive = Boolean(process.stdout.isTTY) && !options.json;
154
+ const withHost = await enrichInitHostOptions(
155
+ { ...options, nameOrPath },
156
+ { interactive }
157
+ );
158
+ const withApprove = await enrichApproveAllOptions(withHost, { interactive });
159
+ const merged = await enrichInitRemoteOptions(withApprove, {
160
+ interactive,
161
+ workspaceName: path.basename(String(nameOrPath || 'workspace'))
162
+ });
163
+ if (merged.cancelled) {
164
+ logger.log('Cancelled.');
165
+ return;
166
+ }
167
+ const json = runInit(merged);
168
+ emitInitReport(json, Boolean(options.json));
169
+ } catch (error) {
170
+ handleCommandError(error, 'agent-kit init');
171
+ process.exitCode = 1;
172
+ }
173
+ });
174
+ }
175
+
176
+ /**
177
+ * @param {import('commander').Command} group
178
+ */
179
+ function addRemoteCommand(group) {
180
+ const remote = group.command('remote').description('Hosting remotes for an Agent Kit workspace');
181
+ remote
182
+ .command('create')
183
+ .description('Create a private GitHub remote and push the current workspace')
184
+ .option('--org <owner>', 'Hosting org or user (required when non-TTY)')
185
+ .option('--name <repo>', 'Remote repository name (default: directory basename)')
186
+ .option('--visibility <mode>', 'private or public', 'private')
187
+ .option('--yes', 'Silent: skip TTY confirmation (pair with --org)')
188
+ .option('--dry-run', 'Plan without creating the remote')
189
+ .option('--json', 'Machine-readable output')
190
+ .addHelpText('after', REMOTE_HELP)
191
+ .action(async(options) => {
192
+ try {
193
+ const interactive = Boolean(process.stdout.isTTY) && !options.json;
194
+ const merged = await enrichRemoteOptions(options, {
195
+ interactive,
196
+ name: options.name || path.basename(process.cwd())
197
+ });
198
+ if (merged.cancelled) {
199
+ logger.log('Cancelled.');
200
+ return;
201
+ }
202
+ const json = runRemoteCreate(merged);
203
+ emitRemoteReport(json, Boolean(options.json));
204
+ } catch (error) {
205
+ handleCommandError(error, 'agent-kit remote create');
206
+ process.exitCode = 1;
207
+ }
208
+ });
209
+ }
210
+
211
+ /**
212
+ * @param {import('commander').Command} group
213
+ */
214
+ function addInstallCommand(group) {
215
+ group
216
+ .command('install')
217
+ .description('Install the Agent Kit into the current git repository')
218
+ .option('--host <target>', 'all, cursor, codex, or claude', 'all')
219
+ .option('--approve-all', 'Write Claude Code and Codex auto-approve configs')
220
+ .option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
221
+ .option('--yes', 'Silent: auto-approve host permission configs without asking')
222
+ .option('--dry-run', 'Plan writes without changing files')
223
+ .option('--json', 'Machine-readable output')
224
+ .addHelpText('after', INSTALL_HELP)
225
+ .action(async(options) => {
226
+ try {
227
+ const interactive = Boolean(process.stdout.isTTY) && !options.json;
228
+ const merged = await enrichApproveAllOptions(options, { interactive });
229
+ await runLeaf(runInstall, merged, true);
230
+ } catch (error) {
231
+ handleCommandError(error, 'agent-kit install');
232
+ process.exitCode = 1;
233
+ }
234
+ });
235
+ }
236
+
237
+ /**
238
+ * @param {import('commander').Command} group
239
+ */
240
+ function addStatusCommand(group) {
241
+ group
242
+ .command('status')
243
+ .description('Show installed Agent Kit version and file state')
244
+ .option('--json', 'Machine-readable output')
245
+ .addHelpText('after', STATUS_HELP)
246
+ .action(async(options) => {
247
+ try {
248
+ await runLeaf(runStatus, options, false);
249
+ } catch (error) {
250
+ handleCommandError(error, 'agent-kit status');
251
+ process.exitCode = 1;
252
+ }
253
+ });
254
+ }
255
+
256
+ /**
257
+ * Recorded --host from a previous install (update omits --host to keep this).
258
+ * @returns {string[]|undefined}
259
+ */
260
+ function readRecordedHosts() {
261
+ try {
262
+ const state = loadState(resolveGitRoot(process.cwd()));
263
+ if (state && Array.isArray(state.hosts) && state.hosts.length) {
264
+ return state.hosts;
265
+ }
266
+ } catch {
267
+ return undefined;
268
+ }
269
+ return undefined;
270
+ }
271
+
272
+ /**
273
+ * Update does not prompt or auto-write host configs unless flags are set.
274
+ * @param {object} options
275
+ * @returns {Promise<object>}
276
+ */
277
+ async function mergeUpdateApproveOptions(options) {
278
+ const interactive = Boolean(process.stdout.isTTY) && !options.json;
279
+ const host = options.host || readRecordedHosts();
280
+ return enrichApproveAllOptions({ ...options, host }, { interactive, ask: false });
281
+ }
282
+
283
+ /**
284
+ * @param {import('commander').Command} group
285
+ */
286
+ function addUpdateCommand(group) {
287
+ group
288
+ .command('update')
289
+ .description('Update managed Agent Kit files from this Builder release')
290
+ .option('--host <target>', 'all, cursor, codex, or claude (omit to keep recorded hosts)')
291
+ .option('--approve-all', 'Write Claude Code and Codex auto-approve configs')
292
+ .option('--skip-approve-all', 'Do not write Claude Code or Codex auto-approve configs')
293
+ .option('--yes', 'Silent: auto-approve host permission configs without asking')
294
+ .option('--dry-run', 'Plan writes without changing files')
295
+ .option('--force', 'Replace modified kit-owned files and managed sections')
296
+ .option('--json', 'Machine-readable output')
297
+ .addHelpText('after', UPDATE_HELP)
298
+ .action(async(options) => {
299
+ try {
300
+ const merged = await mergeUpdateApproveOptions(options);
301
+ await runLeaf(runUpdate, merged, !options.force);
302
+ } catch (error) {
303
+ handleCommandError(error, 'agent-kit update');
304
+ process.exitCode = 1;
305
+ }
306
+ });
307
+ }
308
+
309
+ /**
310
+ * @param {import('commander').Command} group
311
+ */
312
+ function addValidateCommand(group) {
313
+ group
314
+ .command('validate')
315
+ .description('Validate the bundled Agent Kit and the current repository install')
316
+ .option('--json', 'Machine-readable output')
317
+ .addHelpText('after', VALIDATE_HELP)
318
+ .action(async(options) => {
319
+ try {
320
+ await runValidateLeaf(options);
321
+ } catch (error) {
322
+ handleCommandError(error, 'agent-kit validate');
323
+ process.exitCode = 1;
324
+ }
325
+ });
326
+ }
327
+
328
+ /**
329
+ * @param {object} options
330
+ * @returns {Promise<void>}
331
+ */
332
+ async function runValidateLeaf(options) {
333
+ const json = runValidate(options);
334
+ if (options.json) {
335
+ logger.log(JSON.stringify(json, null, 2));
336
+ } else if (json.success) {
337
+ logger.log(formatSuccessLine(`Agent Kit ${json.kitVersion} is valid`));
338
+ } else {
339
+ logger.error(formatBlockingError(json.errors.join('; ')));
340
+ }
341
+ if (!json.success) {
342
+ process.exitCode = 1;
343
+ }
344
+ }
345
+
346
+ module.exports = { setupAgentKitCommands };
@@ -113,9 +113,9 @@ Behavior:
113
113
  - chatgpt registers the ChatGPT callback URL when provided (--callback-url or TTY prompt).
114
114
  It does not rotate the client secret. Remaining ChatGPT steps: Create with logo (cannot change later), Publish, Connect.
115
115
  - claude registers the Claude callback URL (https://claude.ai/api/mcp/auth_callback) and prints
116
- Name and Server URL. Open https://claude.ai/new#settings/customize-connectors and add a
117
- custom connector with those two values. Use channel configure claude --callback-url only if
118
- Claude shows a callback that is not already registered.
116
+ Name, Server URL, and Authentication: OAuth. Open https://claude.ai/new#settings/customize-connectors
117
+ and add a custom connector with those three values (Runtime MCP requires sign-in). Use channel
118
+ configure claude --callback-url only if Claude shows a callback that is not already registered.
119
119
  - mcp-client writes the Runtime MCP URL into the editor MCP config. Pass --target (or --profile)
120
120
  with cursor, vscode, or github-copilot. It creates or reuses the Integration Client and never
121
121
  rotates the client secret. Remaining step: Reload the editor.
@@ -169,8 +169,8 @@ Examples:
169
169
 
170
170
  ${SHARED_TARGET_BEHAVIOR}
171
171
  - claude registers the Claude callback URL (https://claude.ai/api/mcp/auth_callback) and prints
172
- Name and Server URL. Open https://claude.ai/new#settings/customize-connectors and add a
173
- custom connector with those two values.
172
+ Name, Server URL, and Authentication: OAuth. Open https://claude.ai/new#settings/customize-connectors
173
+ and add a custom connector with those three values (Runtime MCP requires sign-in).
174
174
 
175
175
  Requires: aifabrix login
176
176
 
@@ -18,9 +18,10 @@ const { runCapabilityRelate } = require('../datasource/capability/run-capability
18
18
  const { printCapabilitySuccessFooter } = require('./datasource-capability-output');
19
19
 
20
20
  const CAP_RELATE_HELP = `
21
- Adds or replaces one **foreignKeys[]** row (metadata-only; no pipeline). Optional **metadataSchema.properties** stub unless **--skip-metadata-property**.
21
+ Adds or replaces one **foreignKeys[]** row (metadata-only; no pipeline). Optional **metadataSchema.properties** stub unless **--skip-metadata-property**. **--relation-name** defaults to the carrier **--field**.
22
22
 
23
- $ aifabrix datasource capability relate hubspot-deals --relation-name company --to hubspot-companies --field companyId --target-field externalId
23
+ $ aifabrix datasource capability relate pkg-a-facts --to pkg-b-persons --field personId
24
+ $ aifabrix datasource capability relate pkg-a-facts --relation-name personId --to pkg-b-persons --field personId --target-field externalId
24
25
  `;
25
26
 
26
27
  function parseTargetFields(raw) {
@@ -103,8 +104,8 @@ function setupCapabilityRelateCommand(cap) {
103
104
  .description(
104
105
  'Add or replace foreignKeys[] metadata (+ optional metadataSchema property for relation name)'
105
106
  )
106
- .requiredOption('--relation-name <name>', 'FK name (camelCase; unique per datasource)')
107
- .requiredOption('--to <targetDatasource>', 'Target datasource key (cross-system JSON key)')
107
+ .option('--relation-name <name>', 'FK name (camelCase; defaults to --field)')
108
+ .requiredOption('--to <targetDatasource>', 'Target datasource key (sibling packages resolve by key)')
108
109
  .requiredOption('--field <name>', 'Local normalized attribute (foreignKeys.fields[])')
109
110
  .option('--description <text>', 'FK description (defaults to a generated description)')
110
111
  .option('--required', 'Mark FK required (foreignKeys[].required=true)')
@@ -117,7 +117,7 @@ function repairAuthChangeLabel(authOption) {
117
117
  }
118
118
 
119
119
  const MISSING_TEST_ENDPOINT_WARNING =
120
- 'authentication.variables.testEndpoint is missing for apikey/bearerToken auth — add a GET URL for credential/E2E tests (e.g. /crm/v3/objects/contacts?limit=1, or a path resolved against baseUrl).';
120
+ 'authentication.variables.testEndpoint is missing for apikey/bearerToken auth — add a GET URL for credential/E2E tests (use the OpenAPI minimum for required page-size query params, or a path resolved against baseUrl).';
121
121
 
122
122
  /**
123
123
  * Warn when apikey auth (including bearerToken preset) has no testEndpoint.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Skip repair key prefix when a datasource key is already unique scoped kebab-case.
3
+ *
4
+ * @fileoverview Unique kebab datasource keys stay as-is during repair
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const SCOPED_KEBAB = /^[a-z0-9]+(-[a-z0-9]+)+$/;
10
+
11
+ /**
12
+ * @param {string} key
13
+ * @returns {boolean}
14
+ */
15
+ function isScopedKebabKey(key) {
16
+ return SCOPED_KEBAB.test(String(key || '').trim());
17
+ }
18
+
19
+ /**
20
+ * True when key already contains a hyphen and appears once in the package.
21
+ * Single-token keys (persons) must still be prefixed with the system key.
22
+ *
23
+ * @param {string} key
24
+ * @param {string[]} allKeys
25
+ * @returns {boolean}
26
+ */
27
+ function shouldSkipSystemKeyPrefix(key, allKeys) {
28
+ const trimmed = String(key || '').trim();
29
+ if (!isScopedKebabKey(trimmed) || trimmed.endsWith('-datasource')) {
30
+ return false;
31
+ }
32
+ const hits = (allKeys || []).filter((item) => String(item || '').trim() === trimmed);
33
+ return hits.length === 1;
34
+ }
35
+
36
+ module.exports = {
37
+ isScopedKebabKey,
38
+ shouldSkipSystemKeyPrefix
39
+ };
@@ -17,6 +17,7 @@ const fs = require('fs');
17
17
  const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
18
18
  const { backupIntegrationFile } = require('../utils/integration-file-backup');
19
19
  const { trackRepairWrite } = require('./repair-changed-files');
20
+ const { shouldSkipSystemKeyPrefix } = require('./repair-datasource-key-skip');
20
21
 
21
22
  /**
22
23
  * Returns suffix from a canonical-format filename: <systemKey>-datasource-<suffix>.<ext>.
@@ -53,6 +54,9 @@ function isKeyAlreadyCanonical(key, systemKey, fileName) {
53
54
  return true;
54
55
  }
55
56
  }
57
+ if (shouldSkipSystemKeyPrefix(key, [key])) {
58
+ return true;
59
+ }
56
60
  if (!key.startsWith(systemKey + '-')) return false;
57
61
  const suffix = key.slice(systemKey.length + 1);
58
62
  if (!suffix) return false;
@@ -126,6 +130,7 @@ function normalizeDatasourceKeysAndFilenames(appPath, datasourceFiles, systemKey
126
130
 
127
131
  const slugCounts = new Map();
128
132
  const fileInfos = [];
133
+ const packageKeys = [];
129
134
 
130
135
  for (const fileName of datasourceFiles) {
131
136
  const filePath = path.join(appPath, fileName);
@@ -138,23 +143,30 @@ function normalizeDatasourceKeysAndFilenames(appPath, datasourceFiles, systemKey
138
143
  continue;
139
144
  }
140
145
  const key = (parsed && typeof parsed.key === 'string' && parsed.key.trim()) ? parsed.key.trim() : null;
141
- if (isKeyAlreadyCanonical(key, systemKey, fileName) && isFilenameAlreadyCanonical(fileName, systemKey)) {
142
- fileInfos.push({ fileName, key, skip: true });
146
+ if (key) packageKeys.push(key);
147
+ fileInfos.push({ fileName, parsed, key, skip: false, slug: null, canonicalKey: null });
148
+ }
149
+
150
+ for (const info of fileInfos) {
151
+ if (info.skip) continue;
152
+ const filenameCanonical = isFilenameAlreadyCanonical(info.fileName, systemKey);
153
+ const keyCanonical = isKeyAlreadyCanonical(info.key, systemKey, info.fileName);
154
+ const skipPrefix =
155
+ shouldSkipSystemKeyPrefix(info.key, packageKeys) &&
156
+ !String(info.key || '').endsWith('-datasource');
157
+ if ((keyCanonical || skipPrefix) && filenameCanonical) {
158
+ info.skip = true;
159
+ continue;
160
+ }
161
+ if (skipPrefix) {
162
+ info.canonicalKey = info.key;
143
163
  continue;
144
164
  }
145
- const slug = slugFromKey(key || fileName, systemKey);
146
- fileInfos.push({
147
- fileName,
148
- parsed,
149
- key,
150
- slug,
151
- canonicalKey: null,
152
- skip: false
153
- });
165
+ info.slug = slugFromKey(info.key || info.fileName, systemKey);
154
166
  }
155
167
 
156
168
  for (const info of fileInfos) {
157
- if (info.skip) continue;
169
+ if (info.skip || info.canonicalKey) continue;
158
170
  const slug = info.slug;
159
171
  const n = (slugCounts.get(slug) || 0) + 1;
160
172
  slugCounts.set(slug, n);
@@ -29,6 +29,7 @@ const {
29
29
  ensureOrchestrationResourceType
30
30
  } = require('./repair-rbac-platform-preset');
31
31
  const { trackRepairWrite } = require('./repair-changed-files');
32
+ const { listCapabilityKeys } = require('../datasource/capability/capability-manifest');
32
33
  const { ensureDefaultRoles } = require('./repair-rbac-catalog-defaults');
33
34
 
34
35
  const DEFAULT_CAPABILITIES = ['list', 'get', 'create', 'update', 'delete'];
@@ -47,24 +48,29 @@ function _normalizeOperationKey(opKey) {
47
48
  return cleaned.replace(/-+/g, '-').replace(/^-+|-+$/g, '');
48
49
  }
49
50
 
51
+ function defaultCapabilitiesForDatasource(parsed) {
52
+ if (String(parsed?.storageType || '') === 'view') {
53
+ return ['list', 'get'];
54
+ }
55
+ return [...DEFAULT_CAPABILITIES];
56
+ }
57
+
50
58
  /**
51
- * Resolves capabilities from datasource (array or legacy object).
59
+ * Resolves capabilities from datasource (array, COM objects, or legacy map).
52
60
  * @param {Object} parsed - Parsed datasource
53
61
  * @returns {string[]}
54
62
  */
55
63
  function getCapabilitiesFromDatasource(parsed) {
56
- const cap = parsed?.capabilities;
57
- if (Array.isArray(cap)) {
58
- const arr = cap.filter(c => typeof c === 'string');
59
- if (arr.length > 0) return arr;
60
- return [...DEFAULT_CAPABILITIES];
64
+ const fromList = listCapabilityKeys(parsed?.capabilities);
65
+ if (fromList.length > 0) {
66
+ return fromList;
61
67
  }
62
- if (cap && typeof cap === 'object') {
63
- const keys = Object.keys(cap).filter(k => cap[k] === true);
68
+ const cap = parsed?.capabilities;
69
+ if (cap && typeof cap === 'object' && !Array.isArray(cap)) {
70
+ const keys = Object.keys(cap).filter((k) => cap[k] === true);
64
71
  if (keys.length > 0) return keys;
65
- return [...DEFAULT_CAPABILITIES];
66
72
  }
67
- return [...DEFAULT_CAPABILITIES];
73
+ return defaultCapabilitiesForDatasource(parsed);
68
74
  }
69
75
 
70
76
  /**
@@ -0,0 +1,61 @@
1
+ /**
2
+ * TTY and --json output for role-assistant copy.
3
+ *
4
+ * @fileoverview Success and blocking copy envelopes
5
+ */
6
+
7
+ 'use strict';
8
+
9
+ const logger = require('../utils/logger');
10
+ const { formatSuccessLine, formatBlockingError } = require('../utils/cli-layout-chalk');
11
+
12
+ function copyWarnings(result) {
13
+ return Array.isArray(result.warnings) ? result.warnings : [];
14
+ }
15
+
16
+ function buildCopyEnvelope(result) {
17
+ const envelope = {
18
+ success: true,
19
+ path: result.path,
20
+ caseId: result.caseId,
21
+ suiteId: result.suiteId,
22
+ processExecutionId: result.processExecutionId
23
+ };
24
+ const warnings = copyWarnings(result);
25
+ if (warnings.length > 0) {
26
+ envelope.warnings = warnings;
27
+ }
28
+ if (result.dryRun === true) {
29
+ envelope.dryRun = true;
30
+ envelope.yaml = result.yaml;
31
+ }
32
+ return envelope;
33
+ }
34
+
35
+ function emitCopySuccess(options, result) {
36
+ if (options && options.json) {
37
+ logger.log(JSON.stringify(buildCopyEnvelope(result), null, 2));
38
+ return;
39
+ }
40
+ if (result.dryRun === true) {
41
+ logger.log(result.yaml);
42
+ logger.log(formatSuccessLine(`Would write ${result.path}`));
43
+ return;
44
+ }
45
+ logger.log(formatSuccessLine(`Wrote ${result.path}`));
46
+ }
47
+
48
+ function emitCopyFailure(options, error) {
49
+ const message = error && error.message ? error.message : String(error);
50
+ if (options && options.json) {
51
+ logger.log(JSON.stringify({ success: false, error: message }));
52
+ return;
53
+ }
54
+ logger.error(formatBlockingError(message));
55
+ }
56
+
57
+ module.exports = {
58
+ buildCopyEnvelope,
59
+ emitCopySuccess,
60
+ emitCopyFailure
61
+ };