@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,310 @@
1
+ /**
2
+ * Start Claude Code or Codex in a named tmux session.
3
+ * @fileoverview Does not install Agent Kit skills
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const os = require('os');
9
+ const path = require('path');
10
+ const { execFileSync, spawnSync } = require('child_process');
11
+ const logger = require('../utils/logger');
12
+ const { resolveGitRoot } = require('./repo-guard');
13
+ const { writeApproveAllFiles } = require('./host-approve');
14
+
15
+ const START_APPS = {
16
+ claude: { bin: 'claude' },
17
+ codex: { bin: 'codex' }
18
+ };
19
+
20
+ /**
21
+ * @param {unknown} value
22
+ * @returns {string}
23
+ */
24
+ function normalizeStartApp(value) {
25
+ const app = String(value || '').trim();
26
+ if (!START_APPS[app]) {
27
+ throw new Error('Invalid app. Use claude or codex.');
28
+ }
29
+ return app;
30
+ }
31
+
32
+ /**
33
+ * Session names may not contain colons or periods.
34
+ * @param {unknown} value
35
+ * @returns {string}
36
+ */
37
+ function sanitizeSessionName(value) {
38
+ const name = String(value || '')
39
+ .trim()
40
+ .replace(/[.:\s]+/g, '-')
41
+ .replace(/-+/g, '-')
42
+ .replace(/^-|-$/g, '');
43
+ if (!name) {
44
+ throw new Error(
45
+ 'Invalid --name. Use the last folder of this git workspace, for example aifabrix-docs.'
46
+ );
47
+ }
48
+ return name;
49
+ }
50
+
51
+ /**
52
+ * Default display name is the last folder of the git workspace.
53
+ * @param {unknown} explicit
54
+ * @param {string} cwd
55
+ * @returns {string}
56
+ */
57
+ function resolveSessionName(explicit, cwd) {
58
+ const raw = explicit === undefined || explicit === null || explicit === ''
59
+ ? path.basename(cwd)
60
+ : explicit;
61
+ return sanitizeSessionName(raw);
62
+ }
63
+
64
+ /**
65
+ * @param {object} options
66
+ * @returns {string}
67
+ */
68
+ function currentUsername(options) {
69
+ if (options && options.username) {
70
+ return String(options.username);
71
+ }
72
+ try {
73
+ return os.userInfo().username;
74
+ } catch {
75
+ return process.env.USER || process.env.LOGNAME || 'user';
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Hostname + login, e.g. builder01-dev03.
81
+ * @param {object} [options]
82
+ * @returns {string}
83
+ */
84
+ function hostUserPrefix(options) {
85
+ const hostRaw = options && options.hostname ? options.hostname : os.hostname();
86
+ const host = String(hostRaw).split('.')[0];
87
+ return sanitizeSessionName(`${host}-${currentUsername(options || {})}`);
88
+ }
89
+
90
+ /**
91
+ * Remote Control session: builder01-dev03-aifabrix-docs.
92
+ * @param {string} prefix
93
+ * @param {string} session
94
+ * @returns {string}
95
+ */
96
+ function resolveRemoteControlName(prefix, session) {
97
+ return sanitizeSessionName(`${prefix}-${session}`);
98
+ }
99
+
100
+ /**
101
+ * @param {string} bin
102
+ * @param {Function} exec
103
+ * @returns {boolean}
104
+ */
105
+ function hasOnPath(bin, exec) {
106
+ try {
107
+ exec('which', [bin], {
108
+ encoding: 'utf8',
109
+ stdio: ['ignore', 'pipe', 'pipe']
110
+ });
111
+ return true;
112
+ } catch {
113
+ return false;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * @param {object} options
119
+ * @returns {void}
120
+ */
121
+ function validateStartOptions(options) {
122
+ const app = String(options.app || '').trim();
123
+ if (options.remoteControl && app !== 'claude') {
124
+ throw new Error('--remote-control is only valid with claude.');
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Claude argv after the binary (Remote Control names, not Claude --tmux).
130
+ * @param {object} p
131
+ * @returns {string[]}
132
+ */
133
+ function claudeInnerArgs(p) {
134
+ const args = ['--name', p.session];
135
+ if (!p.remoteControl) {
136
+ return args;
137
+ }
138
+ return args.concat([
139
+ '--remote-control',
140
+ p.remoteControlName,
141
+ '--remote-control-session-name-prefix',
142
+ p.remoteControlPrefix
143
+ ]);
144
+ }
145
+
146
+ /**
147
+ * @param {object} p
148
+ * @returns {object}
149
+ */
150
+ function buildTmuxSpec(p) {
151
+ const spec = START_APPS[p.app];
152
+ const extra = p.app === 'claude' ? claudeInnerArgs(p) : [];
153
+ const mode = p.detach
154
+ ? ['new-session', '-d', '-s', p.session, '-c', p.cwd]
155
+ : ['new-session', '-A', '-s', p.session, '-c', p.cwd];
156
+ const remote = p.app === 'claude' && p.remoteControl;
157
+ return {
158
+ bin: 'tmux',
159
+ args: [...mode, spec.bin, ...extra],
160
+ session: p.session,
161
+ appBin: spec.bin,
162
+ needTmux: true,
163
+ remoteControlName: remote ? p.remoteControlName : undefined,
164
+ remoteControlPrefix: remote ? p.remoteControlPrefix : undefined
165
+ };
166
+ }
167
+
168
+ /**
169
+ * @param {object} p
170
+ * @returns {object}
171
+ */
172
+ function buildLaunchSpec(p) {
173
+ return buildTmuxSpec(p);
174
+ }
175
+
176
+ /**
177
+ * @param {{ appBin: string, needTmux?: boolean }} spec
178
+ * @param {Function} exec
179
+ * @returns {void}
180
+ */
181
+ function requireBinaries(spec, exec) {
182
+ if (spec.needTmux && !hasOnPath('tmux', exec)) {
183
+ throw new Error('tmux is not on PATH. Install tmux, then retry.');
184
+ }
185
+ if (hasOnPath(spec.appBin, exec)) {
186
+ return;
187
+ }
188
+ if (spec.appBin === 'claude') {
189
+ throw new Error(
190
+ 'claude is not on PATH. Install with: curl -fsSL https://claude.ai/install.sh | bash'
191
+ );
192
+ }
193
+ throw new Error('codex is not on PATH. Install with: npm install -g @openai/codex');
194
+ }
195
+
196
+ /**
197
+ * @param {object} options
198
+ * @returns {boolean}
199
+ */
200
+ function shouldExecute(options) {
201
+ if (options.dryRun || options.json) {
202
+ return false;
203
+ }
204
+ if (options.detach) {
205
+ return true;
206
+ }
207
+ if (!process.stdout.isTTY) {
208
+ throw new Error('agent-kit start needs a TTY to attach. Pass --detach or --dry-run.');
209
+ }
210
+ return true;
211
+ }
212
+
213
+ /**
214
+ * @param {object} options
215
+ * @param {{ bin: string, args: string[], cwd: string }} spec
216
+ * @returns {unknown}
217
+ */
218
+ function spawnLaunch(options, spec) {
219
+ const spawn = options.spawnSync || spawnSync;
220
+ const result = spawn(spec.bin, spec.args, { stdio: 'inherit', cwd: spec.cwd });
221
+ const status = result && typeof result.status === 'number' ? result.status : 0;
222
+ if (status !== 0) {
223
+ throw new Error(`${spec.bin} exited with status ${status}`);
224
+ }
225
+ return status;
226
+ }
227
+
228
+ /**
229
+ * @param {object} spec
230
+ * @param {object} extra
231
+ * @returns {object}
232
+ */
233
+ function toStartResult(spec, extra) {
234
+ const launch = [spec.bin, ...spec.args];
235
+ return {
236
+ command: 'start',
237
+ app: extra.app,
238
+ session: spec.session,
239
+ remoteControlName: spec.remoteControlName,
240
+ remoteControlPrefix: spec.remoteControlPrefix,
241
+ path: extra.cwd,
242
+ launch,
243
+ tmux: spec.needTmux ? launch : undefined,
244
+ approvals: extra.approvals,
245
+ dryRun: extra.dryRun,
246
+ detach: extra.detach,
247
+ executed: extra.executed
248
+ };
249
+ }
250
+
251
+ /**
252
+ * @param {object} options
253
+ * @returns {object}
254
+ */
255
+ function runStart(options) {
256
+ validateStartOptions(options);
257
+ const app = normalizeStartApp(options.app);
258
+ const cwd = resolveGitRoot(options.cwd || process.cwd());
259
+ const session = resolveSessionName(options.name, cwd);
260
+ const remoteControlPrefix = hostUserPrefix(options);
261
+ const remoteControlName = resolveRemoteControlName(remoteControlPrefix, session);
262
+ const exec = options.execFileSync || execFileSync;
263
+ const spec = buildLaunchSpec({
264
+ app,
265
+ cwd,
266
+ session,
267
+ remoteControlPrefix,
268
+ remoteControlName,
269
+ detach: Boolean(options.detach),
270
+ remoteControl: Boolean(options.remoteControl)
271
+ });
272
+ spec.cwd = cwd;
273
+ const approveAll = !options.skipApproveAll && Boolean(options.approveAll || options.yes);
274
+ const approvals = writeApproveAllFiles({
275
+ repoRoot: cwd,
276
+ hosts: [app],
277
+ approveAll,
278
+ dryRun: Boolean(options.dryRun)
279
+ });
280
+ if (!options.dryRun) {
281
+ requireBinaries(spec, exec);
282
+ }
283
+ const execute = shouldExecute(options);
284
+ if (execute && !options.json) {
285
+ logger.log(`Starting ${app} session ${spec.session}`);
286
+ }
287
+ if (execute) {
288
+ spawnLaunch(options, spec);
289
+ }
290
+ return toStartResult(spec, {
291
+ app,
292
+ cwd,
293
+ approvals,
294
+ dryRun: Boolean(options.dryRun),
295
+ detach: Boolean(options.detach),
296
+ executed: execute
297
+ });
298
+ }
299
+
300
+ module.exports = {
301
+ START_APPS,
302
+ normalizeStartApp,
303
+ sanitizeSessionName,
304
+ resolveSessionName,
305
+ hostUserPrefix,
306
+ resolveRemoteControlName,
307
+ claudeInnerArgs,
308
+ buildTmuxSpec,
309
+ runStart
310
+ };
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Installed Agent Kit state file.
3
+ * @fileoverview Read/write .aifabrix/agent-kit.yaml
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const path = require('path');
9
+ const yaml = require('js-yaml');
10
+ const fs = require('../internal/fs-real-sync');
11
+ const { STATE_RELATIVE, SECTION_ID } = require('./constants');
12
+
13
+ /**
14
+ * @param {string} repoRoot
15
+ * @returns {string}
16
+ */
17
+ function statePath(repoRoot) {
18
+ return path.join(repoRoot, STATE_RELATIVE);
19
+ }
20
+
21
+ /**
22
+ * @param {string} repoRoot
23
+ * @returns {object|null}
24
+ */
25
+ function loadState(repoRoot) {
26
+ const filePath = statePath(repoRoot);
27
+ if (!fs.existsSync(filePath)) {
28
+ return null;
29
+ }
30
+ const parsed = yaml.load(fs.readFileSync(filePath, 'utf8'));
31
+ if (!parsed || typeof parsed !== 'object') {
32
+ return null;
33
+ }
34
+ return parsed;
35
+ }
36
+
37
+ /**
38
+ * @param {object} kit
39
+ * @param {string[]} hosts
40
+ * @param {object[]} ops
41
+ * @param {string} builderVersion
42
+ * @returns {object}
43
+ */
44
+ function buildStateDocument(kit, hosts, ops, builderVersion) {
45
+ const managedFiles = [];
46
+ for (const op of ops) {
47
+ if (op.action === 'delete' || op.action === 'retain' || op.action === 'conflict') {
48
+ continue;
49
+ }
50
+ if (op.action === 'skip' && op.fileState === 'MISSING') {
51
+ continue;
52
+ }
53
+ if (!op.targetRel || op.mode === 'obsolete' || op.mode === 'if-missing') {
54
+ continue;
55
+ }
56
+ const row = { path: op.targetRel, sha256: op.sourceHash };
57
+ if (op.mode === 'managed-section') {
58
+ row.section = SECTION_ID;
59
+ }
60
+ managedFiles.push(row);
61
+ }
62
+ return {
63
+ schemaVersion: '1',
64
+ kitVersion: kit.manifest.kitVersion,
65
+ builderVersion,
66
+ installedAt: new Date().toISOString(),
67
+ hosts,
68
+ managedFiles
69
+ };
70
+ }
71
+
72
+ /**
73
+ * @param {string} repoRoot
74
+ * @param {object} doc
75
+ * @returns {void}
76
+ */
77
+ function writeState(repoRoot, doc) {
78
+ const filePath = statePath(repoRoot);
79
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
80
+ fs.writeFileSync(filePath, yaml.dump(doc, { lineWidth: 120, noRefs: true, sortKeys: false }), 'utf8');
81
+ }
82
+
83
+ module.exports = { statePath, loadState, buildStateDocument, writeState };
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Agent Kit status.
3
+ * @fileoverview Classify installed files without writing
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 } = require('./v1-detect');
14
+ const { loadState } = require('./state-file');
15
+ const { ACTION } = require('./constants');
16
+ const { kitStateName, buildJsonReport } = require('./report');
17
+ const { ALL_HOSTS } = require('./hosts');
18
+
19
+ /**
20
+ * @param {object} options
21
+ * @returns {object}
22
+ */
23
+ function runStatus(options) {
24
+ const repoRoot = resolveGitRoot(options.cwd || process.cwd());
25
+ const kit = loadKitPackage();
26
+ const state = loadState(repoRoot);
27
+ const includeShims = detectV1(repoRoot);
28
+ const hosts = (state && state.hosts) || ALL_HOSTS.slice();
29
+ const includeClaude = hosts.includes('claude');
30
+ const items = listManagedTargets(kit, repoRoot, {
31
+ includeShims: includeShims && hosts.includes('cursor'),
32
+ includeClaude
33
+ });
34
+ classifyItems(items, state, hosts);
35
+ items.push(...listObsoleteV1Items(kit, repoRoot, new Set(items.map((item) => item.targetRel))));
36
+ const ops = items.map((item) => ({ ...item, action: ACTION.SKIP }));
37
+ const kitState = kitStateName(state, kit, ops);
38
+ return buildJsonReport({
39
+ command: 'status',
40
+ kit,
41
+ state,
42
+ hosts,
43
+ ops,
44
+ dryRun: true,
45
+ builderVersion: getCliVersion(),
46
+ kitState
47
+ });
48
+ }
49
+
50
+ module.exports = { runStatus };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Detect v1 Agent Kit files in a customer repository.
3
+ * @fileoverview Name + hash detection for migration
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const path = require('path');
9
+ const fs = require('../internal/fs-real-sync');
10
+ const { V1_MARKERS, FILE_STATE } = require('./constants');
11
+ const { sha256File } = require('./checksums');
12
+
13
+ /**
14
+ * @param {string} repoRoot
15
+ * @returns {boolean}
16
+ */
17
+ function detectV1(repoRoot) {
18
+ return V1_MARKERS.some((rel) => fs.existsSync(path.join(repoRoot, rel)));
19
+ }
20
+
21
+ /**
22
+ * @param {object} kit
23
+ * @param {string} repoRoot
24
+ * @param {Set<string>} managedRels
25
+ * @returns {object[]}
26
+ */
27
+ function listObsoleteV1Items(kit, repoRoot, managedRels) {
28
+ const rows = (kit.inventory && kit.inventory.v1Files) || [];
29
+ const items = [];
30
+ for (const row of rows) {
31
+ if (row.disposition === 'shim' || !row.installedPath) {
32
+ continue;
33
+ }
34
+ if (managedRels.has(row.installedPath)) {
35
+ continue;
36
+ }
37
+ const targetAbs = path.join(repoRoot, row.installedPath);
38
+ if (!fs.existsSync(targetAbs)) {
39
+ continue;
40
+ }
41
+ const hash = sha256File(targetAbs);
42
+ const unchanged = hash === row.sha256;
43
+ items.push({
44
+ sourceAbs: null,
45
+ targetRel: row.installedPath,
46
+ targetAbs,
47
+ mode: 'obsolete',
48
+ sourceHash: row.sha256,
49
+ currentHash: hash,
50
+ fileState: unchanged
51
+ ? FILE_STATE.OBSOLETE_UNCHANGED_MANAGED
52
+ : FILE_STATE.OBSOLETE_MODIFIED_MANAGED,
53
+ v1Owner: row.owner
54
+ });
55
+ }
56
+ return items;
57
+ }
58
+
59
+ /**
60
+ * Replace unmodified v1 command files with thin shims on install.
61
+ * @param {object[]} items
62
+ * @param {object} kit
63
+ * @returns {void}
64
+ */
65
+ function markV1ShimReplacements(items, kit) {
66
+ const byPath = new Map();
67
+ for (const row of (kit.inventory && kit.inventory.v1Files) || []) {
68
+ byPath.set(row.installedPath, row);
69
+ }
70
+ for (const item of items) {
71
+ if (!item.optional || !item.currentHash) {
72
+ continue;
73
+ }
74
+ const row = byPath.get(item.targetRel);
75
+ if (row && row.disposition === 'shim' && item.currentHash === row.sha256) {
76
+ item.replaceOnInstall = true;
77
+ }
78
+ }
79
+ }
80
+
81
+ module.exports = { detectV1, listObsoleteV1Items, markV1ShimReplacements };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Validate bundled Agent Kit package and optional install.
3
+ * @fileoverview Schema, paths, skill names, installed conflicts
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const path = require('path');
9
+ const fs = require('../internal/fs-real-sync');
10
+ const { loadKitPackage } = require('./package-load');
11
+ const { listTreeFiles } = require('./file-list');
12
+ const { runStatus } = require('./status');
13
+ const { KIT_STATE } = require('./constants');
14
+
15
+ const FORBIDDEN_RE = /@[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,}|kv:\/\/[^\s]+\/[A-Za-z0-9+/]{12,}|\/home\/[a-z0-9]+\/(?!dev)/i;
16
+
17
+ /**
18
+ * @param {object} kit
19
+ * @returns {string[]}
20
+ */
21
+ function collectPackageErrors(kit) {
22
+ const errors = [];
23
+ const names = kit.manifest.skills || [];
24
+ if (new Set(names).size !== names.length) {
25
+ errors.push('Duplicate skill name in agent-kit.yaml.');
26
+ }
27
+ for (const name of names) {
28
+ const skillMd = path.join(kit.root, 'skills', name, 'SKILL.md');
29
+ if (!fs.existsSync(skillMd)) {
30
+ errors.push(`Missing SKILL.md for ${name}.`);
31
+ }
32
+ }
33
+ for (const spec of [...(kit.manifest.managed || []), ...(kit.manifest.optional || [])]) {
34
+ const sourceAbs = path.join(kit.root, spec.source);
35
+ if (!fs.existsSync(sourceAbs)) {
36
+ errors.push(`Managed source missing: ${spec.source}`);
37
+ }
38
+ }
39
+ return errors;
40
+ }
41
+
42
+ /**
43
+ * @param {object} kit
44
+ * @returns {string[]}
45
+ */
46
+ function collectContentErrors(kit) {
47
+ const errors = [];
48
+ for (const file of listTreeFiles(kit.root, '')) {
49
+ if (file.rel === 'parity-inventory.yaml') {
50
+ continue;
51
+ }
52
+ const text = fs.readFileSync(file.abs, 'utf8');
53
+ if (FORBIDDEN_RE.test(text)) {
54
+ errors.push(`Forbidden customer/secret pattern in ${file.rel}`);
55
+ }
56
+ }
57
+ return errors;
58
+ }
59
+
60
+ /**
61
+ * @param {object} [options]
62
+ * @returns {object}
63
+ */
64
+ function runValidate(options = {}) {
65
+ const kit = loadKitPackage();
66
+ const errors = [...collectPackageErrors(kit), ...collectContentErrors(kit)];
67
+ let install = null;
68
+ try {
69
+ install = runStatus(options);
70
+ } catch (error) {
71
+ if (!String(error.message || '').includes('git repository')) {
72
+ throw error;
73
+ }
74
+ }
75
+ if (install && install.state === KIT_STATE.CONFLICTS) {
76
+ errors.push(`Install conflicts: ${install.conflicts.join(', ')}`);
77
+ }
78
+ return {
79
+ success: errors.length === 0,
80
+ command: 'validate',
81
+ kitVersion: kit.manifest.kitVersion,
82
+ errors,
83
+ install
84
+ };
85
+ }
86
+
87
+ module.exports = { runValidate, collectPackageErrors, collectContentErrors };
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Resolve workspace directory name for agent-kit init.
3
+ * @fileoverview Name authority — never rewrite cust-* to aif-*
4
+ */
5
+
6
+ 'use strict';
7
+
8
+ const path = require('path');
9
+
10
+ /**
11
+ * @param {string|undefined} nameOrPath
12
+ * @param {string} cwd
13
+ * @returns {{ targetAbs: string, workspaceName: string }}
14
+ */
15
+ function resolveWorkspaceTarget(nameOrPath, cwd) {
16
+ const base = path.resolve(cwd || process.cwd());
17
+ if (!nameOrPath || !String(nameOrPath).trim()) {
18
+ throw new Error('Workspace name or path is required. Example: aifabrix agent-kit init cust-demo');
19
+ }
20
+ const raw = String(nameOrPath).trim();
21
+ const looksLikePath =
22
+ path.isAbsolute(raw) || raw.includes('/') || raw.includes('\\') || raw === '.' || raw.startsWith('.');
23
+ const targetAbs = looksLikePath ? path.resolve(base, raw) : path.join(base, raw);
24
+ const workspaceName = path.basename(targetAbs);
25
+ if (!workspaceName || workspaceName === '.' || workspaceName === '..') {
26
+ throw new Error('Could not derive a workspace name from the path.');
27
+ }
28
+ return { targetAbs, workspaceName };
29
+ }
30
+
31
+ /**
32
+ * Preserve user-supplied names; never rewrite cust-* prefixes.
33
+ * @param {string} name
34
+ * @returns {string}
35
+ */
36
+ function assertNameUnchanged(name) {
37
+ return name;
38
+ }
39
+
40
+ module.exports = { resolveWorkspaceTarget, assertNameUnchanged };