@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.
- package/README.md +8 -0
- package/lib/agent-kit/apply-ops.js +82 -0
- package/lib/agent-kit/checksums.js +28 -0
- package/lib/agent-kit/classify.js +113 -0
- package/lib/agent-kit/claude-shim.js +61 -0
- package/lib/agent-kit/constants.js +55 -0
- package/lib/agent-kit/file-list.js +103 -0
- package/lib/agent-kit/host-approve.js +130 -0
- package/lib/agent-kit/hosting/github.js +63 -0
- package/lib/agent-kit/hosts.js +71 -0
- package/lib/agent-kit/init.js +319 -0
- package/lib/agent-kit/kit-root.js +17 -0
- package/lib/agent-kit/managed-section.js +79 -0
- package/lib/agent-kit/package-load.js +45 -0
- package/lib/agent-kit/plan-ops.js +69 -0
- package/lib/agent-kit/remote-prompts.js +239 -0
- package/lib/agent-kit/render-host-markdown.js +40 -0
- package/lib/agent-kit/repo-guard.js +47 -0
- package/lib/agent-kit/report.js +107 -0
- package/lib/agent-kit/run.js +82 -0
- package/lib/agent-kit/scm/git.js +122 -0
- package/lib/agent-kit/state-file.js +83 -0
- package/lib/agent-kit/status.js +50 -0
- package/lib/agent-kit/v1-detect.js +81 -0
- package/lib/agent-kit/validate-kit.js +87 -0
- package/lib/agent-kit/workspace-name.js +40 -0
- package/lib/agent-kit/workspace-skeleton.js +103 -0
- package/lib/api/role-assistant-test-pack.api.js +86 -0
- package/lib/api/work-search.api.js +31 -4
- package/lib/api/work.api.js +19 -12
- package/lib/app/list-external-systems.js +2 -1
- package/lib/app/show-display.js +33 -6
- package/lib/app/show.js +25 -2
- package/lib/channels/add-channel.js +1 -0
- package/lib/channels/approval-guides/claude.js +2 -2
- package/lib/channels/channel-add-failure.js +1 -0
- package/lib/channels/channel-add-remaining.js +15 -9
- package/lib/channels/channel-definition-status.js +115 -0
- package/lib/channels/channel-description.js +2 -1
- package/lib/channels/channel-install-envelope.js +4 -2
- package/lib/channels/channel-integration-generator.js +2 -0
- package/lib/channels/channel-runtime-resolver.js +14 -0
- package/lib/channels/channel-targets.js +31 -5
- package/lib/channels/claude-setup-values.js +4 -1
- package/lib/channels/slack-channel-add.js +59 -6
- package/lib/channels/slack-mcp-callback.js +51 -0
- package/lib/channels/slack-mcp-runtime.js +156 -0
- package/lib/channels/slack-provisioning/install-cli.js +14 -2
- package/lib/channels/slack-provisioning/with-curl-on-path.js +6 -1
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-certification-commands.js +2 -2
- package/lib/cli/setup-certification.help.js +3 -1
- package/lib/commands/agent-kit-help.js +79 -0
- package/lib/commands/agent-kit.js +346 -0
- package/lib/commands/channel-add-help.js +5 -5
- package/lib/commands/datasource-capability-relate-cli.js +5 -4
- package/lib/commands/repair-auth-apply.js +1 -1
- package/lib/commands/repair-datasource-key-skip.js +39 -0
- package/lib/commands/repair-datasource-keys.js +24 -12
- package/lib/commands/repair-rbac.js +16 -10
- package/lib/commands/role-assistant-copy-output.js +61 -0
- package/lib/commands/role-assistant-copy.js +155 -0
- package/lib/commands/role-assistant-test-output.js +8 -9
- package/lib/commands/role-assistant-test-result-rows.js +42 -0
- package/lib/commands/role-assistant.js +117 -48
- package/lib/commands/search.js +143 -0
- package/lib/commands/test-trust-external.js +7 -5
- package/lib/commands/upload-sidecar-followups.js +4 -1
- package/lib/commands/verify-operations-skip-e2e.js +131 -0
- package/lib/commands/verify-operations-step-plan.js +6 -33
- package/lib/commands/verify-operations-steps.js +3 -1
- package/lib/datasource/capability/relate-operations.js +28 -10
- package/lib/datasource/capability/run-capability-relate.js +48 -25
- package/lib/datasource/datasource-identifier.js +13 -5
- package/lib/deployment/deployer-poll-run.js +117 -35
- package/lib/external-system/deploy-dataplane-published.js +70 -0
- package/lib/external-system/deploy.js +12 -2
- package/lib/external-system/download-role-assistant.js +41 -15
- package/lib/external-system/download.js +7 -1
- package/lib/generator/wizard-datasource-key.js +54 -0
- package/lib/generator/wizard.js +55 -54
- package/lib/integration-definition/apply-datasource-keys.js +75 -0
- package/lib/integration-definition/apply-relationships.js +195 -0
- package/lib/integration-definition/apply-static-enterprise.js +229 -0
- package/lib/integration-definition/apply-system-file.js +71 -0
- package/lib/integration-definition/apply-test-endpoint.js +89 -0
- package/lib/integration-definition/apply-viewpoint-defaults.js +157 -0
- package/lib/integration-definition/apply.js +78 -59
- package/lib/integration-definition/enrich-openapi-operations.js +36 -10
- package/lib/integration-definition/enrich-openapi-pick.js +87 -0
- package/lib/lifecycle/role-assistant-governance.js +56 -21
- package/lib/programmatic/capability-registry.js +1 -0
- package/lib/programmatic/index.js +2 -0
- package/lib/programmatic/openapi-apply-result-schemas.js +8 -0
- package/lib/programmatic/openapi-channel-install-schemas.js +4 -1
- package/lib/programmatic/openapi-component-schemas.js +1 -0
- package/lib/programmatic/openapi-descriptions.js +9 -3
- package/lib/programmatic/openapi-envelope-examples.js +7 -0
- package/lib/programmatic/openapi-operation-bindings.js +5 -0
- package/lib/programmatic/openapi-request-schemas.js +5 -0
- package/lib/programmatic/openapi-schema-descriptions-channel.js +8 -5
- package/lib/programmatic/openapi-schema-descriptions-operations.js +19 -0
- package/lib/programmatic/openapi-spec.js +1 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +7 -0
- package/lib/programmatic/run-channel-catalog.js +50 -8
- package/lib/programmatic/run-channel-install-options.js +5 -1
- package/lib/programmatic/run-download.js +41 -0
- package/lib/protection/auth-context.js +3 -1
- package/lib/protection/protection-display.js +3 -2
- package/lib/protection/report-exit.js +50 -0
- package/lib/protection/run-commands.js +2 -0
- package/lib/role-assistant/package-validate.js +175 -0
- package/lib/role-assistant/test-case-assertion-row.js +36 -0
- package/lib/role-assistant/test-case-copy-bind.js +77 -0
- package/lib/role-assistant/test-case-copy-expected.js +127 -0
- package/lib/role-assistant/test-case-copy-from-work.js +90 -0
- package/lib/role-assistant/test-case-copy-interactions.js +67 -0
- package/lib/role-assistant/test-case-copy.js +119 -0
- package/lib/role-assistant/test-case-expected-catalog.js +135 -0
- package/lib/role-assistant/test-case-validate.js +88 -0
- package/lib/role-assistant/test-cases-bid.js +117 -4
- package/lib/role-assistant/test-cases-evaluate.js +137 -30
- package/lib/role-assistant/test-cases-honesty.js +155 -6
- package/lib/role-assistant/test-cases-lifecycle.js +291 -0
- package/lib/role-assistant/test-cases-search-graph.js +122 -0
- package/lib/role-assistant/test-cases-search.js +20 -2
- package/lib/role-assistant/test-cases.js +34 -168
- package/lib/role-assistant/test-pack-map.js +180 -0
- package/lib/role-assistant/test-pack-sync.js +187 -0
- package/lib/role-assistant/test-runner-debug.js +54 -0
- package/lib/role-assistant/test-runner-respond-body.js +36 -0
- package/lib/role-assistant/test-runner-search.js +20 -5
- package/lib/role-assistant/test-runner-start-payload.js +63 -10
- package/lib/role-assistant/test-runner-workhub-answers.js +45 -16
- package/lib/role-assistant/test-runner-workhub.js +45 -21
- package/lib/role-assistant/test-runner.js +50 -10
- package/lib/schema/external-datasource.schema.json +36 -5
- package/lib/schema/external-system.schema.json +20 -2
- package/lib/schema/role-assistant-test-case.schema.json +408 -0
- package/lib/search/dataplane-context.js +23 -0
- package/lib/search/search-args.js +211 -0
- package/lib/search/search-display.js +238 -0
- package/lib/search/search-errors.js +50 -0
- package/lib/search/search-prompts.js +119 -0
- package/lib/search/search-roles.js +85 -0
- package/lib/search/search-run.js +241 -0
- package/lib/search/search-skills.js +198 -0
- package/lib/utils/authentication-slot-show.js +153 -0
- package/lib/utils/credential-display.js +43 -1
- package/lib/utils/external-system-readiness-compact-blocks.js +5 -1
- package/lib/utils/external-system-readiness-compact-display.js +30 -1
- package/lib/utils/external-system-validators.js +2 -1
- package/lib/utils/help-builder.js +3 -1
- package/lib/utils/schema-loader.js +37 -0
- package/lib/validation/validate-external-file.js +5 -1
- package/lib/validation/validate.js +3 -2
- package/package.json +1 -1
- package/templates/agent-kit/adapters/claude/nested.CLAUDE.md +1 -0
- package/templates/agent-kit/adapters/claude/root.CLAUDE.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-0-plan.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-3-push.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-1-validate.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-2-build.md +5 -0
- package/templates/agent-kit/adapters/cursor/commands/cust-biz-3-prove.md +5 -0
- package/templates/agent-kit/agent-kit.yaml +45 -0
- package/templates/agent-kit/instructions/AGENTKIT.md +83 -0
- package/templates/agent-kit/instructions/integration.AGENTS.md +25 -0
- package/templates/agent-kit/instructions/role-assistant.AGENTS.md +22 -0
- package/templates/agent-kit/instructions/root.AGENTS.md +37 -0
- package/templates/agent-kit/parity-inventory.yaml +188 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/SKILL.md +58 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/contract-checklist.md +40 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/delivery-gates.md +54 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/references/production-safety.md +35 -0
- package/templates/agent-kit/skills/aifabrix-connected-system/scripts/reserved-system-key.js +19 -0
- package/templates/agent-kit/skills/aifabrix-plan/SKILL.md +51 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/challenge-checklist.md +108 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/interaction.md +34 -0
- package/templates/agent-kit/skills/aifabrix-plan/references/plan-template.md +182 -0
- package/templates/agent-kit/skills/aifabrix-prove/SKILL.md +50 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/demo-template.md +65 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/evidence-lifecycle.md +68 -0
- package/templates/agent-kit/skills/aifabrix-prove/references/learning-template.md +66 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/SKILL.md +42 -0
- package/templates/agent-kit/skills/aifabrix-role-assistant/references/package-boundary.md +21 -0
- package/templates/agent-kit/skills/shared/hosts.md +78 -0
- package/templates/agent-kit/workspace/.cursor/plans/README.md +22 -0
- package/templates/agent-kit/workspace/LICENSE.mit +21 -0
- package/templates/agent-kit/workspace/README.md +89 -0
- package/templates/applications/builder-api/Dockerfile +3 -1
- package/templates/applications/dataplane/application.yaml +2 -2
- package/templates/applications/dataplane/env.template +1 -1
- package/templates/applications/dataplane/rbac.yaml +28 -0
- package/templates/applications/keycloak/env.template +3 -3
- package/templates/applications/miso-controller/application.yaml +1 -1
- package/templates/applications/miso-controller/env.template +8 -9
- package/templates/channels/claude/TROUBLESHOOTING.md +3 -0
- package/templates/channels/claude/channel.json +1 -1
- package/templates/channels/claude/install.template.md.hbs +1 -0
- package/templates/channels/claude/integration/claude-system.json +1 -1
- package/templates/channels/slack/channel.json +11 -0
- package/templates/channels/slack/install.template.md.hbs +9 -0
- package/templates/channels/slack/integration/slack-system.json +11 -0
- package/templates/channels/slack/manifest.template.json.hbs +6 -3
- package/templates/channels/teams-bot/channel.json +1 -0
- package/templates/python/docker-compose.hbs +14 -0
- package/templates/typescript/docker-compose.hbs +14 -0
|
@@ -0,0 +1,239 @@
|
|
|
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
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {{ execFileSync?: Function }} [options]
|
|
13
|
+
* @returns {string}
|
|
14
|
+
*/
|
|
15
|
+
function detectGithubLogin(options = {}) {
|
|
16
|
+
const exec = options.execFileSync || execFileSync;
|
|
17
|
+
try {
|
|
18
|
+
const out = exec('gh', ['api', 'user', '-q', '.login'], {
|
|
19
|
+
encoding: 'utf8',
|
|
20
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
21
|
+
});
|
|
22
|
+
return String(out || '').trim();
|
|
23
|
+
} catch {
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string[]} argv
|
|
30
|
+
* @param {string} flag
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
function argvHasFlag(argv, flag) {
|
|
34
|
+
return argv.includes(flag) || argv.some((a) => a.startsWith(`${flag}=`));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Ask which AI coding hosts to install for (TTY only when --host not set).
|
|
39
|
+
* @param {object} options
|
|
40
|
+
* @param {{ interactive?: boolean, inquirer?: object, argv?: string[] }} [deps]
|
|
41
|
+
* @returns {Promise<object>}
|
|
42
|
+
*/
|
|
43
|
+
async function enrichInitHostOptions(options, deps = {}) {
|
|
44
|
+
const interactive = Boolean(deps.interactive);
|
|
45
|
+
const argv = deps.argv || process.argv;
|
|
46
|
+
if (!interactive || argvHasFlag(argv, '--host')) {
|
|
47
|
+
return options;
|
|
48
|
+
}
|
|
49
|
+
const inquirer = deps.inquirer || require('inquirer');
|
|
50
|
+
const { host } = await inquirer.prompt([
|
|
51
|
+
{
|
|
52
|
+
type: 'list',
|
|
53
|
+
name: 'host',
|
|
54
|
+
message: 'Which AI coding platforms should Agent Kit support?',
|
|
55
|
+
default: 'all',
|
|
56
|
+
choices: [
|
|
57
|
+
{
|
|
58
|
+
name: `All (${ALL_HOSTS.map((h) => HOST_LABELS[h] || h).join(', ')})`,
|
|
59
|
+
value: 'all'
|
|
60
|
+
},
|
|
61
|
+
...ALL_HOSTS.map((h) => ({
|
|
62
|
+
name: HOST_LABELS[h] || h,
|
|
63
|
+
value: h
|
|
64
|
+
}))
|
|
65
|
+
]
|
|
66
|
+
}
|
|
67
|
+
]);
|
|
68
|
+
return { ...options, host: host || 'all' };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {object} deps
|
|
73
|
+
* @returns {Promise<import('inquirer').Answers>}
|
|
74
|
+
*/
|
|
75
|
+
async function askRemoteFields(deps) {
|
|
76
|
+
const inquirer = deps.inquirer || require('inquirer');
|
|
77
|
+
const defaultOrg = deps.defaultOrg || '';
|
|
78
|
+
return inquirer.prompt([
|
|
79
|
+
{
|
|
80
|
+
type: 'input',
|
|
81
|
+
name: 'org',
|
|
82
|
+
message: 'GitHub org or user:',
|
|
83
|
+
default: defaultOrg || undefined,
|
|
84
|
+
when: () => !deps.org,
|
|
85
|
+
validate: (v) => (String(v || '').trim() ? true : 'Org or user is required')
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: 'list',
|
|
89
|
+
name: 'visibility',
|
|
90
|
+
message: 'Repository visibility:',
|
|
91
|
+
choices: [
|
|
92
|
+
{ name: 'private (recommended)', value: 'private' },
|
|
93
|
+
{ name: 'public', value: 'public' }
|
|
94
|
+
],
|
|
95
|
+
default: 'private',
|
|
96
|
+
when: () => !deps.visibilitySet
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: 'confirm',
|
|
100
|
+
name: 'confirmed',
|
|
101
|
+
message: (answers) => {
|
|
102
|
+
const org = deps.org || answers.org;
|
|
103
|
+
const vis = deps.visibility || answers.visibility || 'private';
|
|
104
|
+
const full = `${org}/${deps.name}`;
|
|
105
|
+
return `Create github:${full} (${vis}) and push?`;
|
|
106
|
+
},
|
|
107
|
+
default: false,
|
|
108
|
+
when: () => !deps.yes
|
|
109
|
+
}
|
|
110
|
+
]);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Fill org/visibility and confirm on TTY. Non-interactive requires --org.
|
|
115
|
+
* @param {object} options
|
|
116
|
+
* @param {{ interactive?: boolean, name?: string, inquirer?: object, execFileSync?: Function, argv?: string[] }} [deps]
|
|
117
|
+
* @returns {Promise<object>}
|
|
118
|
+
*/
|
|
119
|
+
async function enrichRemoteOptions(options, deps = {}) {
|
|
120
|
+
const interactive = Boolean(deps.interactive);
|
|
121
|
+
const name = deps.name || options.name || 'repo';
|
|
122
|
+
const argv = deps.argv || process.argv;
|
|
123
|
+
const visibilitySet = argvHasFlag(argv, '--visibility');
|
|
124
|
+
let org = options.org;
|
|
125
|
+
let visibility = options.visibility || 'private';
|
|
126
|
+
|
|
127
|
+
if (!interactive) {
|
|
128
|
+
if (!org) {
|
|
129
|
+
throw new Error('Non-interactive remote create requires --org <owner>.');
|
|
130
|
+
}
|
|
131
|
+
return { ...options, org, visibility, name };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const defaultOrg = org || detectGithubLogin({ execFileSync: deps.execFileSync });
|
|
135
|
+
const answers = await askRemoteFields({
|
|
136
|
+
inquirer: deps.inquirer,
|
|
137
|
+
org,
|
|
138
|
+
name,
|
|
139
|
+
visibility,
|
|
140
|
+
visibilitySet,
|
|
141
|
+
defaultOrg,
|
|
142
|
+
yes: Boolean(options.yes)
|
|
143
|
+
});
|
|
144
|
+
org = String(org || answers.org || '').trim();
|
|
145
|
+
if (answers.visibility) {
|
|
146
|
+
visibility = answers.visibility;
|
|
147
|
+
}
|
|
148
|
+
if (!org) {
|
|
149
|
+
throw new Error('Org or user is required.');
|
|
150
|
+
}
|
|
151
|
+
if (options.yes !== true && answers.confirmed === false) {
|
|
152
|
+
return { ...options, org, visibility, name, cancelled: true };
|
|
153
|
+
}
|
|
154
|
+
return { ...options, org, visibility, name, remote: true };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* On TTY without --remote/--no-remote, ask whether to create a remote.
|
|
159
|
+
* @param {object} options
|
|
160
|
+
* @param {{ interactive?: boolean, inquirer?: object, execFileSync?: Function, workspaceName?: string }} [deps]
|
|
161
|
+
* @returns {Promise<object>}
|
|
162
|
+
*/
|
|
163
|
+
async function enrichInitRemoteOptions(options, deps = {}) {
|
|
164
|
+
const interactive = Boolean(deps.interactive);
|
|
165
|
+
const remoteExplicit = options.remote === true;
|
|
166
|
+
const noRemote = options.noRemote === true || options.remote === false;
|
|
167
|
+
|
|
168
|
+
if (noRemote || !interactive) {
|
|
169
|
+
return options;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
let wantRemote = remoteExplicit;
|
|
173
|
+
if (!remoteExplicit) {
|
|
174
|
+
const inquirer = deps.inquirer || require('inquirer');
|
|
175
|
+
const { createRemote } = await inquirer.prompt([
|
|
176
|
+
{
|
|
177
|
+
type: 'confirm',
|
|
178
|
+
name: 'createRemote',
|
|
179
|
+
message: 'Create a private GitHub remote and push now?',
|
|
180
|
+
default: false
|
|
181
|
+
}
|
|
182
|
+
]);
|
|
183
|
+
wantRemote = Boolean(createRemote);
|
|
184
|
+
}
|
|
185
|
+
if (!wantRemote) {
|
|
186
|
+
return { ...options, remote: false };
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return enrichRemoteOptions(
|
|
190
|
+
{ ...options, remote: true },
|
|
191
|
+
{
|
|
192
|
+
interactive: true,
|
|
193
|
+
name: deps.workspaceName || options.nameOrPath,
|
|
194
|
+
inquirer: deps.inquirer,
|
|
195
|
+
execFileSync: deps.execFileSync
|
|
196
|
+
}
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* TTY asks; --approve-all / --yes / non-TTY auto-approve; --skip-approve-all skips.
|
|
202
|
+
* @param {object} options
|
|
203
|
+
* @param {{ interactive?: boolean, inquirer?: object, ask?: boolean }} [deps]
|
|
204
|
+
* @returns {Promise<object>}
|
|
205
|
+
*/
|
|
206
|
+
async function enrichApproveAllOptions(options, deps = {}) {
|
|
207
|
+
const hosts = normalizeHosts(options.host);
|
|
208
|
+
const needsHost = hosts.includes('claude') || hosts.includes('codex');
|
|
209
|
+
if (!needsHost || options.skipApproveAll) {
|
|
210
|
+
return { ...options, approveAll: false };
|
|
211
|
+
}
|
|
212
|
+
if (options.approveAll || options.yes) {
|
|
213
|
+
return { ...options, approveAll: true };
|
|
214
|
+
}
|
|
215
|
+
if (deps.ask === false) {
|
|
216
|
+
return { ...options, approveAll: false };
|
|
217
|
+
}
|
|
218
|
+
if (!deps.interactive) {
|
|
219
|
+
return { ...options, approveAll: true };
|
|
220
|
+
}
|
|
221
|
+
const inquirer = deps.inquirer || require('inquirer');
|
|
222
|
+
const { approveAll } = await inquirer.prompt([
|
|
223
|
+
{
|
|
224
|
+
type: 'confirm',
|
|
225
|
+
name: 'approveAll',
|
|
226
|
+
message: 'Auto-approve all Claude Code and Codex tool permissions in this workspace?',
|
|
227
|
+
default: true
|
|
228
|
+
}
|
|
229
|
+
]);
|
|
230
|
+
return { ...options, approveAll: Boolean(approveAll) };
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
module.exports = {
|
|
234
|
+
detectGithubLogin,
|
|
235
|
+
enrichInitHostOptions,
|
|
236
|
+
enrichRemoteOptions,
|
|
237
|
+
enrichInitRemoteOptions,
|
|
238
|
+
enrichApproveAllOptions
|
|
239
|
+
};
|
|
@@ -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: Boolean(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
|
+
};
|