@visulima/vis 1.0.0-alpha.11 → 1.0.0-alpha.13

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 (116) hide show
  1. package/CHANGELOG.md +101 -0
  2. package/LICENSE.md +559 -186
  3. package/README.md +18 -0
  4. package/dist/bin.js +1 -9
  5. package/dist/config/index.d.ts +477 -556
  6. package/dist/config/index.js +1 -2
  7. package/dist/generate/index.js +1 -3
  8. package/dist/packem_chunks/applyDefaults.js +2 -336
  9. package/dist/packem_chunks/bin.js +234 -9552
  10. package/dist/packem_chunks/doctor-probe.js +2 -112
  11. package/dist/packem_chunks/fix.js +11 -234
  12. package/dist/packem_chunks/handler.js +1 -99
  13. package/dist/packem_chunks/handler10.js +2 -53
  14. package/dist/packem_chunks/handler11.js +1 -32
  15. package/dist/packem_chunks/handler12.js +5 -100
  16. package/dist/packem_chunks/handler13.js +1 -25
  17. package/dist/packem_chunks/handler14.js +18 -916
  18. package/dist/packem_chunks/handler15.js +15 -201
  19. package/dist/packem_chunks/handler16.js +1 -124
  20. package/dist/packem_chunks/handler17.js +1 -13
  21. package/dist/packem_chunks/handler18.js +1 -106
  22. package/dist/packem_chunks/handler19.js +1 -19
  23. package/dist/packem_chunks/handler2.js +2 -75
  24. package/dist/packem_chunks/handler20.js +5 -29
  25. package/dist/packem_chunks/handler21.js +1 -222
  26. package/dist/packem_chunks/handler22.js +1 -237
  27. package/dist/packem_chunks/handler23.js +5 -101
  28. package/dist/packem_chunks/handler24.js +1 -110
  29. package/dist/packem_chunks/handler25.js +3 -402
  30. package/dist/packem_chunks/handler26.js +1 -13
  31. package/dist/packem_chunks/handler27.js +1 -63
  32. package/dist/packem_chunks/handler28.js +7 -34
  33. package/dist/packem_chunks/handler29.js +21 -456
  34. package/dist/packem_chunks/handler3.js +4 -95
  35. package/dist/packem_chunks/handler30.js +3 -170
  36. package/dist/packem_chunks/handler31.js +1 -530
  37. package/dist/packem_chunks/handler32.js +2 -214
  38. package/dist/packem_chunks/handler33.js +25 -119
  39. package/dist/packem_chunks/handler34.js +2 -630
  40. package/dist/packem_chunks/handler35.js +3 -283
  41. package/dist/packem_chunks/handler36.js +22 -542
  42. package/dist/packem_chunks/handler37.js +410 -744
  43. package/dist/packem_chunks/handler38.js +22 -989
  44. package/dist/packem_chunks/handler39.js +22 -574
  45. package/dist/packem_chunks/handler4.js +2 -90
  46. package/dist/packem_chunks/handler40.js +22 -1685
  47. package/dist/packem_chunks/handler41.js +6 -1088
  48. package/dist/packem_chunks/handler42.js +5 -797
  49. package/dist/packem_chunks/handler43.js +10 -2658
  50. package/dist/packem_chunks/handler44.js +51 -3784
  51. package/dist/packem_chunks/handler45.js +25 -2574
  52. package/dist/packem_chunks/handler46.js +3 -3769
  53. package/dist/packem_chunks/handler47.js +21 -1485
  54. package/dist/packem_chunks/handler48.js +42 -0
  55. package/dist/packem_chunks/handler5.js +8 -174
  56. package/dist/packem_chunks/handler6.js +1 -95
  57. package/dist/packem_chunks/handler7.js +1 -115
  58. package/dist/packem_chunks/handler8.js +1 -12
  59. package/dist/packem_chunks/handler9.js +1 -29
  60. package/dist/packem_chunks/heal-accept.js +10 -522
  61. package/dist/packem_chunks/heal.js +14 -673
  62. package/dist/packem_chunks/index.js +7 -873
  63. package/dist/packem_chunks/loader.js +1 -23
  64. package/dist/packem_chunks/tar.js +3 -0
  65. package/dist/packem_shared/ai-analysis-hm8d2W7z.js +67 -0
  66. package/dist/packem_shared/ai-cache-DoiF80AR.js +1 -0
  67. package/dist/packem_shared/ai-fix-nn4zOE95.js +43 -0
  68. package/dist/packem_shared/cache-directory-CwHlJhgx.js +1 -0
  69. package/dist/packem_shared/dependency-scan-COr5n63B.js +2 -0
  70. package/dist/packem_shared/docker-D6OGr5_S.js +2 -0
  71. package/dist/packem_shared/failure-log-iUVLf6ts.js +2 -0
  72. package/dist/packem_shared/flakiness-D9wf0t56.js +1 -0
  73. package/dist/packem_shared/giget-CcEy_Elm.js +2 -0
  74. package/dist/packem_shared/index-DH-5hsrC.js +1 -0
  75. package/dist/packem_shared/otel-DxDUPJJH.js +6 -0
  76. package/dist/packem_shared/otelPlugin-CQq6poq8.js +1 -0
  77. package/dist/packem_shared/registry-CkubDdiY.js +2 -0
  78. package/dist/packem_shared/run-summary-utils-BfBvjzhY.js +1 -0
  79. package/dist/packem_shared/runtime-check-BXZ43CBW.js +1 -0
  80. package/dist/packem_shared/selectors-BylODRiM.js +3 -0
  81. package/dist/packem_shared/symbols-CQmER5MT.js +1 -0
  82. package/dist/packem_shared/toolchain-BgBOUHII.js +5 -0
  83. package/dist/packem_shared/typosquats-CcZl99B1.js +1 -0
  84. package/dist/packem_shared/use-measured-height-DjYgUOKk.js +1 -0
  85. package/dist/packem_shared/utils-DrNg0XTR.js +1 -0
  86. package/dist/packem_shared/verify-Baj5mFJ7.js +1 -0
  87. package/dist/packem_shared/vis-update-app-D1jl0UZZ.js +1 -0
  88. package/dist/packem_shared/xxh3-DrAUNq4n.js +1 -0
  89. package/index.js +556 -727
  90. package/package.json +19 -29
  91. package/schemas/project.schema.json +739 -297
  92. package/schemas/vis-config.schema.json +3365 -384
  93. package/templates/buildkite-ci/template.yml +20 -20
  94. package/dist/packem_shared/VisUpdateApp-D-Yz_wvg.js +0 -1316
  95. package/dist/packem_shared/_commonjsHelpers-BqLXS_qQ.js +0 -5
  96. package/dist/packem_shared/ai-analysis-CHeB1joD.js +0 -367
  97. package/dist/packem_shared/ai-cache-Be_jexe4.js +0 -142
  98. package/dist/packem_shared/ai-fix-B9iQVcD2.js +0 -379
  99. package/dist/packem_shared/cache-directory-2qvs4goY.js +0 -98
  100. package/dist/packem_shared/catalog-BJTtyi-O.js +0 -1371
  101. package/dist/packem_shared/dependency-scan-A0KSklpG.js +0 -188
  102. package/dist/packem_shared/docker-2iZzc280.js +0 -181
  103. package/dist/packem_shared/failure-log-Cz3Z4SKL.js +0 -100
  104. package/dist/packem_shared/flakiness-goTxXuCX.js +0 -180
  105. package/dist/packem_shared/otel-DCvqCTz_.js +0 -158
  106. package/dist/packem_shared/otelPlugin-DFaLDvJf.js +0 -3
  107. package/dist/packem_shared/registry-CbqXI0rc.js +0 -272
  108. package/dist/packem_shared/run-summary-utils-PVMl4aIh.js +0 -130
  109. package/dist/packem_shared/runtime-check-Cobi3p6l.js +0 -127
  110. package/dist/packem_shared/selectors-SM69TfqC.js +0 -194
  111. package/dist/packem_shared/symbols-Ta7g2nU-.js +0 -14
  112. package/dist/packem_shared/toolchain-BdZd9eBi.js +0 -975
  113. package/dist/packem_shared/typosquats-C-bCh3PX.js +0 -1210
  114. package/dist/packem_shared/use-measured-height-CNP0vT4M.js +0 -20
  115. package/dist/packem_shared/utils-CthVdBPS.js +0 -40
  116. package/dist/packem_shared/xxh3-Ck8mXNg1.js +0 -239
@@ -1,12 +1 @@
1
- import { r as resolveInstaller, k as runDedupe } from './bin.js';
2
-
3
- const execute = async ({ logger, options, visConfig, workspaceRoot: wsRoot }) => {
4
- const cwd = wsRoot ?? process.cwd();
5
- const pm = resolveInstaller(cwd, { configBackend: visConfig?.install?.backend });
6
- const code = runDedupe(pm, options.check || false, cwd, logger);
7
- if (code !== 0) {
8
- process.exitCode = code;
9
- }
10
- };
11
-
12
- export { execute as default };
1
+ var i=Object.defineProperty;var n=(e,o)=>i(e,"name",{value:o,configurable:!0});import{m as l,d as p}from"./bin.js";var f=Object.defineProperty,d=n((e,o)=>f(e,"name",{value:o,configurable:!0}),"n");const m=d(async({logger:e,options:o,visConfig:a,workspaceRoot:r})=>{const c=r??process.cwd(),t=l(c,{configBackend:a?.install?.backend,configCorepack:a?.install?.corepack}),s=p(t,o.check||!1,c,e);s!==0&&(process.exitCode=s)},"execute");export{m as default};
@@ -1,29 +1 @@
1
- import { r as resolveInstaller, l as runDlx } from './bin.js';
2
-
3
- const execute = async ({ argument, logger, options, visConfig, workspaceRoot: wsRoot }) => {
4
- const args = argument;
5
- if (!args || args.length === 0) {
6
- throw new Error("No package specified. Usage: vis dlx <package[@version]> [args...]");
7
- }
8
- const [pkg, ...rest] = args;
9
- const cwd = wsRoot ?? process.cwd();
10
- const pm = resolveInstaller(cwd, { configBackend: visConfig?.install?.backend });
11
- const additionalPackages = options.package ? Array.isArray(options.package) ? options.package : [options.package] : [];
12
- const code = runDlx(
13
- pm,
14
- {
15
- additionalPackages,
16
- args: rest,
17
- package: pkg,
18
- shellMode: options.shellMode || false,
19
- silent: options.silent || false
20
- },
21
- cwd,
22
- logger
23
- );
24
- if (code !== 0) {
25
- process.exitCode = code;
26
- }
27
- };
28
-
29
- export { execute as default };
1
+ var d=Object.defineProperty;var i=(a,s)=>d(a,"name",{value:s,configurable:!0});import{m as f,e as m}from"./bin.js";var u=Object.defineProperty,v=i((a,s)=>u(a,"name",{value:s,configurable:!0}),"n");const x=v(async({argument:a,logger:s,options:e,visConfig:r,workspaceRoot:t})=>{const o=a;if(!o||o.length===0)throw new Error("No package specified. Usage: vis dlx <package[@version]> [args...]");const[g,...l]=o,c=t??process.cwd(),p=f(c,{configBackend:r?.install?.backend,configCorepack:r?.install?.corepack}),k=e.package?Array.isArray(e.package)?e.package:[e.package]:[],n=m(p,{additionalPackages:k,args:l,package:g,shellMode:e.shellMode||!1,silent:e.silent||!1},c,s);n!==0&&(process.exitCode=n)},"execute");export{x as default};
@@ -1,522 +1,10 @@
1
- import { createRequire as __cjs_createRequire } from "node:module";
2
-
3
- const __cjs_require = __cjs_createRequire(import.meta.url);
4
-
5
- const __cjs_getProcess = typeof globalThis !== "undefined" && typeof globalThis.process !== "undefined" ? globalThis.process : process;
6
-
7
- const __cjs_getBuiltinModule = (module) => {
8
- // Check if we're in Node.js and version supports getBuiltinModule
9
- if (typeof __cjs_getProcess !== "undefined" && __cjs_getProcess.versions && __cjs_getProcess.versions.node) {
10
- const [major, minor] = __cjs_getProcess.versions.node.split(".").map(Number);
11
- // Node.js 20.16.0+ and 22.3.0+
12
- if (major > 22 || (major === 22 && minor >= 3) || (major === 20 && minor >= 16)) {
13
- return __cjs_getProcess.getBuiltinModule(module);
14
- }
15
- }
16
- // Fallback to createRequire
17
- return __cjs_require(module);
18
- };
19
-
20
- const {
21
- readFile
22
- } = __cjs_getBuiltinModule("node:fs/promises");
23
- import { resolve, relative } from '@visulima/path';
24
- import { d as detectCiContext, f as findHealCandidate, p as proposeAndApply, v as validateAppliedFix, a as postPrComment } from './heal.js';
25
- const {
26
- createInterface
27
- } = __cjs_getBuiltinModule("node:readline");
28
- import { c as detectPm, F as runAdd, p as pail } from './bin.js';
29
-
30
- const isInteractive = () => Boolean(process.stdout.isTTY) && process.env.CI !== "true";
31
- const defaultPrompt = (question) => new Promise((resolve) => {
32
- const rl = createInterface({ input: process.stdin, output: process.stderr });
33
- rl.question(`${question} (Y/n) `, (answer) => {
34
- rl.close();
35
- const trimmed = answer.trim().toLowerCase();
36
- resolve(trimmed === "" || trimmed === "y" || trimmed === "yes");
37
- });
38
- });
39
- const defaultRunInstall = (sdk, workspaceRoot) => {
40
- const pm = detectPm(workspaceRoot);
41
- const exitCode = runAdd(
42
- pm,
43
- {
44
- exact: false,
45
- filter: [],
46
- global: false,
47
- optional: false,
48
- packages: [sdk],
49
- peer: false,
50
- // Save into "dependencies" (saveDev=false) — the user wants
51
- // this available in CI without `--include=dev` toggling.
52
- // The SDK is a runtime dep of `vis ai heal accept`,
53
- // conceptually.
54
- saveDev: false,
55
- workspace: false,
56
- workspaceRoot: false
57
- },
58
- workspaceRoot,
59
- // eslint-disable-next-line no-console
60
- console
61
- );
62
- return Promise.resolve({ exitCode });
63
- };
64
- const installCommandFor = (sdk) => `pnpm add ${sdk}`;
65
- const defaultImport = (sdk) => {
66
- if (sdk === "@gitbeaker/rest") {
67
- return import('@gitbeaker/rest');
68
- }
69
- return import('@octokit/rest');
70
- };
71
- const loadOptionalSdk = async (sdk, options = {}) => {
72
- const interactive = options.interactive ?? isInteractive();
73
- const prompt = options.prompt ?? defaultPrompt;
74
- const runInstall = options.runInstall ?? defaultRunInstall;
75
- const importImpl = options.importImpl ?? defaultImport;
76
- const workspaceRoot = options.workspaceRoot ?? process.cwd();
77
- try {
78
- return await importImpl(sdk);
79
- } catch (error) {
80
- const code = error.code;
81
- if (code !== "ERR_MODULE_NOT_FOUND" && code !== "MODULE_NOT_FOUND") {
82
- throw error;
83
- }
84
- }
85
- if (!interactive) {
86
- throw new Error(
87
- `${sdk} is not installed. \`vis ai heal accept\` needs it to talk to the host. Install it in your repo first:
88
- ${installCommandFor(sdk)}`
89
- );
90
- }
91
- const accepted = await prompt(`${sdk} isn't installed. Install it now?`);
92
- if (!accepted) {
93
- throw new Error(`${sdk} install declined. Re-run \`vis ai heal accept\` after installing manually:
94
- ${installCommandFor(sdk)}`);
95
- }
96
- const result = await runInstall(sdk, workspaceRoot);
97
- if (result.exitCode !== 0) {
98
- throw new Error(`Install of ${sdk} failed (exit ${String(result.exitCode)}). Install manually and retry:
99
- ${installCommandFor(sdk)}`);
100
- }
101
- return await importImpl(sdk);
102
- };
103
-
104
- const splitGithubRepo = (repo) => {
105
- const slash = repo.indexOf("/");
106
- if (slash <= 0 || slash === repo.length - 1) {
107
- throw new Error(`Expected GITHUB_REPOSITORY in "owner/repo" form, got: ${repo}`);
108
- }
109
- return { owner: repo.slice(0, slash), repo: repo.slice(slash + 1) };
110
- };
111
- const loadGithubClient = async (token, options) => {
112
- if (options.githubClient) {
113
- return options.githubClient;
114
- }
115
- const loader = options.loadSdk ?? loadOptionalSdk;
116
- const module_ = await loader("@octokit/rest");
117
- const OctokitCtor = module_.Octokit;
118
- if (!OctokitCtor) {
119
- throw new TypeError("Loaded `@octokit/rest` but no `Octokit` export was found. Reinstall the package or pin to a supported major.");
120
- }
121
- return new OctokitCtor({ auth: token });
122
- };
123
- const loadGitlabClient = async (token, host, options) => {
124
- if (options.gitlabClient) {
125
- return options.gitlabClient;
126
- }
127
- const loader = options.loadSdk ?? loadOptionalSdk;
128
- const module_ = await loader("@gitbeaker/rest");
129
- const GitlabCtor = module_.Gitlab;
130
- if (!GitlabCtor) {
131
- throw new TypeError("Loaded `@gitbeaker/rest` but no `Gitlab` export was found. Reinstall the package or pin to a supported major.");
132
- }
133
- return new GitlabCtor({ host, token });
134
- };
135
- const apiBaseToHost = (apiBaseUrl) => {
136
- return apiBaseUrl.replace(/\/api\/v\d+\/?$/, "");
137
- };
138
- const commitToGithub = async (options) => {
139
- const { branch, ciContext, files, message, workspaceRoot } = options;
140
- const readFile$1 = options.readFile ?? ((absolute) => readFile(absolute, "utf8"));
141
- if (!ciContext.repo) {
142
- throw new Error("Cannot commit on GitHub: GITHUB_REPOSITORY (owner/repo) is not set.");
143
- }
144
- if (!ciContext.token) {
145
- throw new Error("Cannot commit on GitHub: GITHUB_TOKEN is not set. Grant the workflow `contents: write` and pass the token through.");
146
- }
147
- const { owner, repo } = splitGithubRepo(ciContext.repo);
148
- const client = await loadGithubClient(ciContext.token, options);
149
- const refResponse = await client.rest.git.getRef({ owner, ref: `heads/${branch}`, repo });
150
- const parentSha = refResponse.data.object.sha;
151
- const commitResponse = await client.rest.git.getCommit({ commit_sha: parentSha, owner, repo });
152
- const baseTreeSha = commitResponse.data.tree.sha;
153
- const blobs = await Promise.all(
154
- files.map(async (file) => {
155
- const absolute = resolve(workspaceRoot, file);
156
- const contents = await readFile$1(absolute);
157
- const blob = await client.rest.git.createBlob({
158
- content: Buffer.from(contents, "utf8").toString("base64"),
159
- encoding: "base64",
160
- owner,
161
- repo
162
- });
163
- return { mode: "100644", path: file, sha: blob.data.sha, type: "blob" };
164
- })
165
- );
166
- const tree = await client.rest.git.createTree({ base_tree: baseTreeSha, owner, repo, tree: blobs });
167
- const newCommit = await client.rest.git.createCommit({ message, owner, parents: [parentSha], repo, tree: tree.data.sha });
168
- await client.rest.git.updateRef({ owner, ref: `heads/${branch}`, repo, sha: newCommit.data.sha });
169
- return { sha: newCommit.data.sha, url: newCommit.data.html_url };
170
- };
171
- const commitToGitlab = async (options) => {
172
- const { branch, ciContext, files, message, workspaceRoot } = options;
173
- const readFile$1 = options.readFile ?? ((absolute) => readFile(absolute, "utf8"));
174
- if (!ciContext.repo) {
175
- throw new Error("Cannot commit on GitLab: CI_PROJECT_ID / CI_PROJECT_PATH is not set.");
176
- }
177
- if (!ciContext.token) {
178
- throw new Error("Cannot commit on GitLab: no token. CI_JOB_TOKEN cannot push commits — set GITLAB_TOKEN to a PAT/project-token with `api` scope.");
179
- }
180
- if (!ciContext.apiBaseUrl) {
181
- throw new Error("Cannot commit on GitLab: CI_API_V4_URL is not set.");
182
- }
183
- const host = apiBaseToHost(ciContext.apiBaseUrl);
184
- const client = await loadGitlabClient(ciContext.token, host, options);
185
- const actions = await Promise.all(
186
- files.map(async (file) => {
187
- const absolute = resolve(workspaceRoot, file);
188
- const contents = await readFile$1(absolute);
189
- return { action: "update", content: contents, filePath: file };
190
- })
191
- );
192
- const result = await client.Commits.create(ciContext.repo, branch, message, actions);
193
- const sha = result.id ?? result.sha;
194
- if (!sha) {
195
- throw new Error("GitLab Commits.create returned no commit ID — cannot reference the new commit.");
196
- }
197
- return { sha, url: result.web_url ?? result.webUrl };
198
- };
199
- const commitFiles = async (options) => {
200
- if (options.files.length === 0) {
201
- throw new Error("Cannot commit: no files to include.");
202
- }
203
- if (options.ciContext.provider === "github-actions") {
204
- return await commitToGithub(options);
205
- }
206
- if (options.ciContext.provider === "gitlab-ci") {
207
- return await commitToGitlab(options);
208
- }
209
- throw new Error(`Cannot commit: unsupported CI provider \`${options.ciContext.provider}\`. Run \`vis ai heal accept\` from a recognised CI environment.`);
210
- };
211
-
212
- const TRIGGER_PHRASE = "/vis heal accept";
213
- const loadGithubTrigger = async (eventPath) => {
214
- if (!eventPath) {
215
- return void 0;
216
- }
217
- let raw;
218
- try {
219
- raw = await readFile(eventPath, "utf8");
220
- } catch {
221
- return void 0;
222
- }
223
- const payload = JSON.parse(raw);
224
- const body = payload.comment?.body ?? "";
225
- const actor = payload.comment?.user?.login ?? "";
226
- const headRepo = payload.pull_request?.head?.repo?.full_name;
227
- const baseRepo = payload.pull_request?.base?.repo?.full_name ?? payload.repository?.full_name;
228
- const isFork = headRepo !== void 0 && baseRepo !== void 0 && headRepo !== baseRepo;
229
- return {
230
- actor,
231
- body,
232
- headRef: payload.pull_request?.head?.ref,
233
- isFork
234
- };
235
- };
236
- const loadGitlabTrigger = (env) => {
237
- const body = env.VIS_HEAL_TRIGGER_BODY;
238
- const actor = env.VIS_HEAL_TRIGGER_ACTOR;
239
- if (!body || !actor) {
240
- return void 0;
241
- }
242
- return {
243
- actor,
244
- body,
245
- // GitLab forks (called "subgroups" in the public docs) are
246
- // detected at the host level — the bridge should refuse to
247
- // forward note events from forks. We don't have visibility from
248
- // here, so default to false and rely on the API auth scope.
249
- headRef: env.VIS_HEAL_HEAD_REF,
250
- isFork: false
251
- };
252
- };
253
- const loadBuildkiteTrigger = (env) => {
254
- const actor = env.BUILDKITE_UNBLOCKER_EMAIL ?? env.BUILDKITE_UNBLOCKER;
255
- if (!actor) {
256
- return void 0;
257
- }
258
- return {
259
- actor,
260
- body: TRIGGER_PHRASE,
261
- // BUILDKITE_BRANCH is the source branch in the upstream remote
262
- // namespace. Unlike GitHub Actions (which exposes a synthetic
263
- // `refs/pull/<n>/merge` ref via GITHUB_REF on PR builds),
264
- // Buildkite always reports the underlying branch — exactly what
265
- // the heal commit needs to land on. For non-PR builds it's the
266
- // pushed branch, same target either way.
267
- headRef: env.BUILDKITE_BRANCH,
268
- // Buildkite doesn't model forks at this layer; whether the
269
- // upstream PR is from a fork is a property of the originating
270
- // VCS (GitHub/GitLab), checked when we derive the commit context.
271
- isFork: false
272
- };
273
- };
274
- const loadTrigger = async (ciContext, env) => {
275
- if (ciContext.provider === "github-actions") {
276
- return await loadGithubTrigger(env.GITHUB_EVENT_PATH);
277
- }
278
- if (ciContext.provider === "gitlab-ci") {
279
- return loadGitlabTrigger(env);
280
- }
281
- if (ciContext.provider === "buildkite") {
282
- return loadBuildkiteTrigger(env);
283
- }
284
- return void 0;
285
- };
286
- const deriveBuildkiteCommitContext = (buildkite, env) => {
287
- const repoUrl = env.BUILDKITE_REPO;
288
- if (!repoUrl) {
289
- return void 0;
290
- }
291
- const githubMatch = /(?:^|@|\/\/)github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?\/?$/i.exec(repoUrl);
292
- if (githubMatch && env.GITHUB_TOKEN) {
293
- return {
294
- apiBaseUrl: void 0,
295
- buildId: void 0,
296
- buildNumber: void 0,
297
- prNumber: buildkite.prNumber,
298
- provider: "github-actions",
299
- repo: `${githubMatch[1]}/${githubMatch[2]}`,
300
- sha: buildkite.sha,
301
- token: env.GITHUB_TOKEN
302
- };
303
- }
304
- const gitlabHttpsMatch = /\/\/([^/]*gitlab[^/]*)\/([^/]+)\/(.+?)(?:\.git)?\/?$/i.exec(repoUrl);
305
- const gitlabSshMatch = gitlabHttpsMatch ? null : /@([^/:]*gitlab[^/:]*):([^/]+)\/(.+?)(?:\.git)?\/?$/i.exec(repoUrl);
306
- const gitlabMatch = gitlabHttpsMatch ?? gitlabSshMatch;
307
- if (gitlabMatch && env.GITLAB_TOKEN) {
308
- const host = gitlabMatch[1];
309
- const apiBaseUrl = env.CI_API_V4_URL ?? `https://${host}/api/v4`;
310
- return {
311
- apiBaseUrl,
312
- buildId: void 0,
313
- buildNumber: void 0,
314
- prNumber: buildkite.prNumber,
315
- provider: "gitlab-ci",
316
- repo: `${gitlabMatch[2]}/${gitlabMatch[3]}`,
317
- sha: buildkite.sha,
318
- token: env.GITLAB_TOKEN
319
- };
320
- }
321
- return void 0;
322
- };
323
- const fetchGithubHeadRef = async (options) => {
324
- const fetchImpl = options.fetchImpl ?? globalThis.fetch;
325
- const base = options.apiBaseUrl ?? "https://api.github.com";
326
- const url = `${base.replace(/\/+$/, "")}/repos/${options.repo}/pulls/${String(options.prNumber)}`;
327
- try {
328
- const response = await fetchImpl(url, {
329
- headers: {
330
- Accept: "application/vnd.github+json",
331
- Authorization: `Bearer ${options.token}`,
332
- "X-GitHub-Api-Version": "2022-11-28"
333
- }
334
- });
335
- if (!response.ok) {
336
- return void 0;
337
- }
338
- const json = await response.json();
339
- return json.head?.ref;
340
- } catch {
341
- return void 0;
342
- }
343
- };
344
- const summariseDetail = (commit, files, failingTaskId, actor) => {
345
- const fileLines = files.map((file) => `- \`${file}\``).join("\n");
346
- return [
347
- "## vis ai heal — committed",
348
- "",
349
- `Accepted by @${actor}.`,
350
- "",
351
- `Failing task: \`${failingTaskId}\``,
352
- commit.url ? `Commit: [\`${commit.sha.slice(0, 7)}\`](${commit.url})` : `Commit: \`${commit.sha.slice(0, 7)}\``,
353
- "",
354
- "### Files changed",
355
- fileLines,
356
- "",
357
- "_Re-run the failing job to confirm the fix landed._"
358
- ].join("\n");
359
- };
360
- const acceptHeal = async (toolbox, deps = {}) => {
361
- const { logger, visConfig, workspaceRoot: wsRoot } = toolbox;
362
- const workspaceRoot = wsRoot ?? process.cwd();
363
- const env = deps.env ?? process.env;
364
- const ciContext = await (deps.detectCi ?? (() => detectCiContext(env)))();
365
- if (ciContext.provider === "unknown") {
366
- pail.error("`vis ai heal accept` must run inside a recognised CI provider (GitHub Actions, GitLab CI, or Buildkite).");
367
- process.exitCode = 1;
368
- return;
369
- }
370
- const trigger = await loadTrigger(ciContext, env);
371
- if (!trigger) {
372
- const message2 = ciContext.provider === "github-actions" ? "No issue_comment payload found. Trigger this command from a workflow listening for `issue_comment.created`." : ciContext.provider === "gitlab-ci" ? "No GitLab trigger payload found. Set VIS_HEAL_TRIGGER_BODY, VIS_HEAL_TRIGGER_ACTOR, and VIS_HEAL_HEAD_REF in the bridge that re-emits note hooks as pipeline runs." : "No Buildkite unblock signal found. Wire this command to run after a manually-unblocked block step so BUILDKITE_UNBLOCKER_EMAIL is set.";
373
- pail.error(message2);
374
- process.exitCode = 1;
375
- return;
376
- }
377
- if (ciContext.provider !== "buildkite" && !trigger.body.includes(TRIGGER_PHRASE)) {
378
- pail.notice(`Trigger comment does not contain \`${TRIGGER_PHRASE}\`; nothing to do.`);
379
- return;
380
- }
381
- const aiConfig = visConfig?.ai;
382
- const allowedActors = aiConfig?.heal?.allowedActors ?? [];
383
- if (allowedActors.length === 0) {
384
- pail.error("`ai.heal.allowedActors` is empty. Configure the allow-list in `vis.config.*` before enabling auto-commit.");
385
- process.exitCode = 1;
386
- return;
387
- }
388
- if (!trigger.actor || !allowedActors.includes(trigger.actor)) {
389
- const hint = ciContext.provider === "buildkite" ? "Buildkite entries are emails (BUILDKITE_UNBLOCKER_EMAIL) or Buildkite usernames (BUILDKITE_UNBLOCKER), not the upstream GitHub/GitLab username." : ciContext.provider === "gitlab-ci" ? "GitLab entries are platform usernames (without the leading `@`)." : "GitHub entries are platform usernames (without the leading `@`).";
390
- pail.error(`Actor \`${trigger.actor || "(unknown)"}\` is not in \`ai.heal.allowedActors\`. Refusing to commit. ${hint}`);
391
- process.exitCode = 1;
392
- return;
393
- }
394
- if (trigger.isFork) {
395
- pail.error("Refusing to accept: the change is from a forked repository. The CI token does not have write access to the fork.");
396
- process.exitCode = 1;
397
- return;
398
- }
399
- let branch = trigger.headRef;
400
- if (!branch && ciContext.provider === "github-actions" && ciContext.prNumber !== void 0 && ciContext.repo && ciContext.token) {
401
- branch = await fetchGithubHeadRef({
402
- fetchImpl: deps.fetchImpl,
403
- prNumber: ciContext.prNumber,
404
- repo: ciContext.repo,
405
- token: ciContext.token
406
- });
407
- }
408
- if (!branch) {
409
- pail.error("Could not resolve the PR / MR head branch. Ensure the trigger payload includes head.ref or set VIS_HEAL_HEAD_REF.");
410
- process.exitCode = 1;
411
- return;
412
- }
413
- pail.info(`Accepting fix on \`${branch}\` for actor \`${trigger.actor}\`.`);
414
- const candidateResult = await findHealCandidate(workspaceRoot, toolbox.options.run);
415
- if (candidateResult.outcome === "no-failed-task") {
416
- pail.error("No failed tasks found in the run summary. The accept command should run on the same workspace as the original failure.");
417
- process.exitCode = 1;
418
- return;
419
- }
420
- if (candidateResult.outcome === "missing-metadata") {
421
- pail.error(`Failed task ${candidateResult.failedTask.taskId} is missing project/target metadata; cannot validate the fix.`);
422
- process.exitCode = 1;
423
- return;
424
- }
425
- if (candidateResult.outcome === "no-failure-context") {
426
- pail.error(`No failure log or run summary found for ${candidateResult.failedTask.taskId}.`);
427
- process.exitCode = 1;
428
- return;
429
- }
430
- const candidate = candidateResult;
431
- const proposeResult = await proposeAndApply(toolbox, candidate);
432
- if (proposeResult.outcome === "no-proposal") {
433
- pail.error("AI fix proposal failed; cannot commit.");
434
- process.exitCode = 1;
435
- return;
436
- }
437
- if (proposeResult.outcome === "cannot-fix") {
438
- pail.warn(`AI declined to fix: ${proposeResult.detail ?? "(no reason)"}. Nothing to commit.`);
439
- process.exitCode = 1;
440
- return;
441
- }
442
- if (proposeResult.outcome === "empty-patches") {
443
- pail.warn("AI returned an empty patch set. Nothing to commit.");
444
- process.exitCode = 1;
445
- return;
446
- }
447
- if (proposeResult.outcome === "no-patches-applied") {
448
- pail.error("Patches could not be applied to the workspace. Refusing to commit.");
449
- process.exitCode = 1;
450
- return;
451
- }
452
- pail.info(`Re-running ${candidate.failedTask.taskId} to validate the fix before committing...`);
453
- const validation = await validateAppliedFix(toolbox, candidate, { validate: deps.validate });
454
- if (validation.exitCode !== 0) {
455
- pail.error(`Validation failed (exit ${String(validation.exitCode)}). Refusing to commit.`);
456
- if (validation.stderr.trim().length > 0) {
457
- logger.info("--- validation stderr (tail) ---");
458
- logger.info(validation.stderr.split("\n").slice(-20).join("\n"));
459
- }
460
- process.exitCode = 1;
461
- return;
462
- }
463
- pail.success("Validation passed. Committing.");
464
- const appliedFiles = (proposeResult.applyResults ?? []).filter((result) => result.status === "applied").map((result) => {
465
- const absolute = result.absolutePath ?? result.patch.file;
466
- const rel = relative(workspaceRoot, absolute);
467
- return rel === "" || rel.startsWith("..") ? result.patch.file : rel;
468
- });
469
- if (appliedFiles.length === 0) {
470
- pail.error("No applied files to commit. Aborting.");
471
- process.exitCode = 1;
472
- return;
473
- }
474
- const message = [
475
- `fix: vis ai heal accepted by @${trigger.actor}`,
476
- "",
477
- `Failing task: ${candidate.failedTask.taskId}`,
478
- proposeResult.proposal?.explanation ? `
479
- ${proposeResult.proposal.explanation}` : "",
480
- "",
481
- "Auto-committed by `vis ai heal accept`."
482
- ].join("\n");
483
- let commitContext = ciContext;
484
- if (ciContext.provider === "buildkite") {
485
- const derived = deriveBuildkiteCommitContext(ciContext, env);
486
- if (!derived) {
487
- pail.error(
488
- "Cannot determine the upstream VCS to commit to. Buildkite jobs need GITHUB_TOKEN or GITLAB_TOKEN set (matching the host parsed from BUILDKITE_REPO) for `vis ai heal accept` to land the commit."
489
- );
490
- process.exitCode = 1;
491
- return;
492
- }
493
- commitContext = derived;
494
- }
495
- let commit;
496
- try {
497
- commit = await commitFiles({
498
- branch,
499
- ciContext: commitContext,
500
- files: appliedFiles,
501
- message,
502
- workspaceRoot,
503
- ...deps.commitOverrides
504
- });
505
- } catch (error) {
506
- pail.error(`Commit failed: ${error instanceof Error ? error.message : String(error)}`);
507
- process.exitCode = 1;
508
- return;
509
- }
510
- pail.success(`Committed as ${commit.sha.slice(0, 7)}${commit.url ? ` — ${commit.url}` : ""}.`);
511
- const confirmationBody = summariseDetail(commit, appliedFiles, candidate.failedTask.taskId, trigger.actor);
512
- const postCommentImpl = deps.postComment ?? (async (body, context) => postPrComment({ body, context }));
513
- const postResult = await postCommentImpl(confirmationBody, ciContext);
514
- if (!postResult.posted && postResult.method !== "skipped") {
515
- pail.warn(`Commit landed but the confirmation comment failed: ${postResult.error ?? "unknown"}`);
516
- }
517
- };
518
- const aiHealAccept = async (toolbox) => {
519
- await acceptHeal(toolbox);
520
- };
521
-
522
- export { TRIGGER_PHRASE, aiHealAccept, deriveBuildkiteCommitContext as deriveBuildkiteCommitContextForTesting, fetchGithubHeadRef as fetchGithubHeadRefForTesting, loadBuildkiteTrigger as loadBuildkiteTriggerForTesting, loadGithubTrigger as loadGithubTriggerForTesting, loadGitlabTrigger as loadGitlabTriggerForTesting, acceptHeal as runHealAcceptForTesting, summariseDetail as summariseDetailForTesting };
1
+ var H=Object.defineProperty;var k=(e,t)=>H(e,"name",{value:t,configurable:!0});import{createRequire as U}from"node:module";import{resolve as O,relative as F}from"@visulima/path";import{d as D,P as K,R as S,I as j,p as V}from"./heal.js";import{A as q,G as Y,p as a}from"./bin.js";const P=U(import.meta.url),T=typeof globalThis<"u"&&typeof globalThis.process<"u"?globalThis.process:process,A=k(e=>{if(typeof T<"u"&&T.versions&&T.versions.node){const[t,o]=T.versions.node.split(".").map(Number);if(t>22||t===22&&o>=3||t===20&&o>=16)return T.getBuiltinModule(e)}return P(e)},"__cjs_getBuiltinModule"),{readFile:B}=A("node:fs/promises"),{createInterface:M}=A("node:readline");var J=Object.defineProperty,I=k((e,t)=>J(e,"name",{value:t,configurable:!0}),"o");const W=I(()=>!!process.stdout.isTTY&&process.env.CI!=="true","isInteractive"),z=I(e=>new Promise(t=>{const o=M({input:process.stdin,output:process.stderr});o.question(`${e} (Y/n) `,i=>{o.close();const s=i.trim().toLowerCase();t(s===""||s==="y"||s==="yes")})}),"defaultPrompt"),X=I((e,t)=>{const o=q(t),i=Y(o,{exact:!1,filter:[],global:!1,optional:!1,packages:[e],peer:!1,saveDev:!1,workspace:!1,workspaceRoot:!1},t,console);return Promise.resolve({exitCode:i})},"defaultRunInstall"),y=I(e=>`pnpm add ${e}`,"installCommandFor"),Q=I(e=>e==="@gitbeaker/rest"?import("@gitbeaker/rest"):import("@octokit/rest"),"defaultImport"),L=I(async(e,t={})=>{const o=t.interactive??W(),i=t.prompt??z,s=t.runInstall??X,l=t.importImpl??Q,d=t.workspaceRoot??process.cwd();try{return await l(e)}catch(n){const{code:u}=n;if(u!=="ERR_MODULE_NOT_FOUND"&&u!=="MODULE_NOT_FOUND")throw n}if(!o)throw new Error(`${e} is not installed. \`vis ai heal accept\` needs it to talk to the host. Install it in your repo first:
2
+ ${y(e)}`);if(!await i(`${e} isn't installed. Install it now?`))throw new Error(`${e} install declined. Re-run \`vis ai heal accept\` after installing manually:
3
+ ${y(e)}`);const r=await s(e,d);if(r.exitCode!==0)throw new Error(`Install of ${e} failed (exit ${String(r.exitCode)}). Install manually and retry:
4
+ ${y(e)}`);return await l(e)},"loadOptionalSdk");var Z=Object.defineProperty,C=k((e,t)=>Z(e,"name",{value:t,configurable:!0}),"s");const ee=C(e=>{const t=e.indexOf("/");if(t<=0||t===e.length-1)throw new Error(`Expected GITHUB_REPOSITORY in "owner/repo" form, got: ${e}`);return{owner:e.slice(0,t),repo:e.slice(t+1)}},"splitGithubRepo"),te=C(async(e,t)=>{if(t.githubClient)return t.githubClient;const o=(await(t.loadSdk??L)("@octokit/rest")).Octokit;if(!o)throw new TypeError("Loaded `@octokit/rest` but no `Octokit` export was found. Reinstall the package or pin to a supported major.");return new o({auth:e})},"loadGithubClient"),oe=C(async(e,t,o)=>{if(o.gitlabClient)return o.gitlabClient;const i=(await(o.loadSdk??L)("@gitbeaker/rest")).Gitlab;if(!i)throw new TypeError("Loaded `@gitbeaker/rest` but no `Gitlab` export was found. Reinstall the package or pin to a supported major.");return new i({host:t,token:e})},"loadGitlabClient"),re=C(e=>e.replace(/\/api\/v\d+\/?$/,""),"apiBaseToHost"),ie=C(async e=>{const{branch:t,ciContext:o,files:i,message:s,workspaceRoot:l}=e,d=e.readFile??(w=>B(w,"utf8"));if(!o.repo)throw new Error("Cannot commit on GitHub: GITHUB_REPOSITORY (owner/repo) is not set.");if(!o.token)throw new Error("Cannot commit on GitHub: GITHUB_TOKEN is not set. Grant the workflow `contents: write` and pass the token through.");const{owner:r,repo:n}=ee(o.repo),u=await te(o.token,e),p=(await u.rest.git.getRef({owner:r,ref:`heads/${t}`,repo:n})).data.object.sha,h=(await u.rest.git.getCommit({commit_sha:p,owner:r,repo:n})).data.tree.sha,m=await Promise.all(i.map(async w=>{const E=O(l,w),v=await d(E),_=await u.rest.git.createBlob({content:Buffer.from(v,"utf8").toString("base64"),encoding:"base64",owner:r,repo:n});return{mode:"100644",path:w,sha:_.data.sha,type:"blob"}})),f=await u.rest.git.createTree({base_tree:h,owner:r,repo:n,tree:m}),b=await u.rest.git.createCommit({message:s,owner:r,parents:[p],repo:n,tree:f.data.sha});return await u.rest.git.updateRef({owner:r,ref:`heads/${t}`,repo:n,sha:b.data.sha}),{sha:b.data.sha,url:b.data.html_url}},"commitToGithub"),ae=C(async e=>{const{branch:t,ciContext:o,files:i,message:s,workspaceRoot:l}=e,d=e.readFile??(m=>B(m,"utf8"));if(!o.repo)throw new Error("Cannot commit on GitLab: CI_PROJECT_ID / CI_PROJECT_PATH is not set.");if(!o.token)throw new Error("Cannot commit on GitLab: no token. CI_JOB_TOKEN cannot push commits — set GITLAB_TOKEN to a PAT/project-token with `api` scope.");if(!o.apiBaseUrl)throw new Error("Cannot commit on GitLab: CI_API_V4_URL is not set.");const r=re(o.apiBaseUrl),n=await oe(o.token,r,e),u=await Promise.all(i.map(async m=>{const f=O(l,m);return{action:"update",content:await d(f),filePath:m}})),p=await n.Commits.create(o.repo,t,s,u),h=p.id??p.sha;if(!h)throw new Error("GitLab Commits.create returned no commit ID — cannot reference the new commit.");return{sha:h,url:p.web_url??p.webUrl}},"commitToGitlab"),ne=C(async e=>{if(e.files.length===0)throw new Error("Cannot commit: no files to include.");if(e.ciContext.provider==="github-actions")return await ie(e);if(e.ciContext.provider==="gitlab-ci")return await ae(e);throw new Error(`Cannot commit: unsupported CI provider \`${e.ciContext.provider}\`. Run \`vis ai heal accept\` from a recognised CI environment.`)},"commitFiles");var se=Object.defineProperty,g=k((e,t)=>se(e,"name",{value:t,configurable:!0}),"u");const G="/vis heal accept",ce=g(async e=>{if(!e)return;let t;try{t=await B(e,"utf8")}catch{return}const o=JSON.parse(t),i=o.comment?.body??"",s=o.comment?.user?.login??"",l=o.pull_request?.head?.repo?.full_name,d=o.pull_request?.base?.repo?.full_name??o.repository?.full_name,r=l!==void 0&&d!==void 0&&l!==d;return{actor:s,body:i,headRef:o.pull_request?.head?.ref,isFork:r}},"loadGithubTrigger"),le=g(e=>{const t=e.VIS_HEAL_TRIGGER_BODY,o=e.VIS_HEAL_TRIGGER_ACTOR;if(!(!t||!o))return{actor:o,body:t,headRef:e.VIS_HEAL_HEAD_REF,isFork:!1}},"loadGitlabTrigger"),de=g(e=>{const t=e.BUILDKITE_UNBLOCKER_EMAIL??e.BUILDKITE_UNBLOCKER;if(t)return{actor:t,body:G,headRef:e.BUILDKITE_BRANCH,isFork:!1}},"loadBuildkiteTrigger"),ue=g(async(e,t)=>{if(e.provider==="github-actions")return await ce(t.GITHUB_EVENT_PATH);if(e.provider==="gitlab-ci")return le(t);if(e.provider==="buildkite")return de(t)},"loadTrigger"),pe=g((e,t)=>{const o=t.BUILDKITE_REPO;if(!o)return;const i=/(?:^|@|\/\/)github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?\/?$/i.exec(o);if(i&&t.GITHUB_TOKEN)return{apiBaseUrl:void 0,buildId:void 0,buildNumber:void 0,prNumber:e.prNumber,provider:"github-actions",repo:`${i[1]}/${i[2]}`,sha:e.sha,token:t.GITHUB_TOKEN};const s=/\/\/([^/]*gitlab[^/]*)\/([^/]+)\/(.+?)(?:\.git)?\/?$/i.exec(o),l=s?null:/@([^/:]*gitlab[^/:]*):([^/]+)\/(.+?)(?:\.git)?\/?$/i.exec(o),d=s??l;if(d&&t.GITLAB_TOKEN){const r=d[1];return{apiBaseUrl:t.CI_API_V4_URL??`https://${r}/api/v4`,buildId:void 0,buildNumber:void 0,prNumber:e.prNumber,provider:"gitlab-ci",repo:`${d[2]}/${d[3]}`,sha:e.sha,token:t.GITLAB_TOKEN}}},"deriveBuildkiteCommitContext"),me=g(async e=>{const t=e.fetchImpl??globalThis.fetch,o=`${(e.apiBaseUrl??"https://api.github.com").replace(/\/+$/,"")}/repos/${e.repo}/pulls/${String(e.prNumber)}`;try{const i=await t(o,{headers:{Accept:"application/vnd.github+json",Authorization:`Bearer ${e.token}`,"X-GitHub-Api-Version":"2022-11-28"}});return i.ok?(await i.json()).head?.ref:void 0}catch{return}},"fetchGithubHeadRef"),fe=g((e,t,o,i)=>{const s=t.map(l=>`- \`${l}\``).join(`
5
+ `);return["## vis ai heal committed","",`Accepted by @${i}.`,"",`Failing task: \`${o}\``,e.url?`Commit: [\`${e.sha.slice(0,7)}\`](${e.url})`:`Commit: \`${e.sha.slice(0,7)}\``,"","### Files changed",s,"","_Re-run the failing job to confirm the fix landed._"].join(`
6
+ `)},"summariseDetail"),he=g(async(e,t={})=>{const{logger:o,visConfig:i,workspaceRoot:s}=e,l=s??process.cwd(),d=t.env??process.env,r=await(t.detectCi??(()=>D(d)))();if(r.provider==="unknown"){a.error("`vis ai heal accept` must run inside a recognised CI provider (GitHub Actions, GitLab CI, or Buildkite)."),process.exitCode=1;return}const n=await ue(r,d);if(!n){const c=r.provider==="github-actions"?"No issue_comment payload found. Trigger this command from a workflow listening for `issue_comment.created`.":r.provider==="gitlab-ci"?"No GitLab trigger payload found. Set VIS_HEAL_TRIGGER_BODY, VIS_HEAL_TRIGGER_ACTOR, and VIS_HEAL_HEAD_REF in the bridge that re-emits note hooks as pipeline runs.":"No Buildkite unblock signal found. Wire this command to run after a manually-unblocked block step so BUILDKITE_UNBLOCKER_EMAIL is set.";a.error(c),process.exitCode=1;return}if(r.provider!=="buildkite"&&!n.body.includes(G)){a.notice(`Trigger comment does not contain \`${G}\`; nothing to do.`);return}const u=i?.ai?.heal?.allowedActors??[];if(u.length===0){a.error("`ai.heal.allowedActors` is empty. Configure the allow-list in `vis.config.*` before enabling auto-commit."),process.exitCode=1;return}if(!n.actor||!u.includes(n.actor)){const c=r.provider==="buildkite"?"Buildkite entries are emails (BUILDKITE_UNBLOCKER_EMAIL) or Buildkite usernames (BUILDKITE_UNBLOCKER), not the upstream GitHub/GitLab username.":r.provider==="gitlab-ci"?"GitLab entries are platform usernames (without the leading `@`).":"GitHub entries are platform usernames (without the leading `@`).";a.error(`Actor \`${n.actor||"(unknown)"}\` is not in \`ai.heal.allowedActors\`. Refusing to commit. ${c}`),process.exitCode=1;return}if(n.isFork){a.error("Refusing to accept: the change is from a forked repository. The CI token does not have write access to the fork."),process.exitCode=1;return}let p=n.headRef;if(!p&&r.provider==="github-actions"&&r.prNumber!==void 0&&r.repo&&r.token&&(p=await me({fetchImpl:t.fetchImpl,prNumber:r.prNumber,repo:r.repo,token:r.token})),!p){a.error("Could not resolve the PR / MR head branch. Ensure the trigger payload includes head.ref or set VIS_HEAL_HEAD_REF."),process.exitCode=1;return}a.info(`Accepting fix on \`${p}\` for actor \`${n.actor}\`.`);const h=await K(l,e.options.run);if(h.outcome==="no-failed-task"){a.error("No failed tasks found in the run summary. The accept command should run on the same workspace as the original failure."),process.exitCode=1;return}if(h.outcome==="missing-metadata"){a.error(`Failed task ${h.failedTask.taskId} is missing project/target metadata; cannot validate the fix.`),process.exitCode=1;return}if(h.outcome==="no-failure-context"){a.error(`No failure log or run summary found for ${h.failedTask.taskId}.`),process.exitCode=1;return}const m=h,f=await S(e,m);if(f.outcome==="no-proposal"){a.error("AI fix proposal failed; cannot commit."),process.exitCode=1;return}if(f.outcome==="cannot-fix"){a.warn(`AI declined to fix: ${f.detail??"(no reason)"}. Nothing to commit.`),process.exitCode=1;return}if(f.outcome==="empty-patches"){a.warn("AI returned an empty patch set. Nothing to commit."),process.exitCode=1;return}if(f.outcome==="no-patches-applied"){a.error("Patches could not be applied to the workspace. Refusing to commit."),process.exitCode=1;return}a.info(`Re-running ${m.failedTask.taskId} to validate the fix before committing...`);const b=await j(e,m,{validate:t.validate});if(b.exitCode!==0){a.error(`Validation failed (exit ${String(b.exitCode)}). Refusing to commit.`),b.stderr.trim().length>0&&(o.info("--- validation stderr (tail) ---"),o.info(b.stderr.split(`
7
+ `).slice(-20).join(`
8
+ `))),process.exitCode=1;return}a.success("Validation passed. Committing.");const w=(f.applyResults??[]).filter(c=>c.status==="applied").map(c=>{const $=c.absolutePath??c.patch.file,x=F(l,$);return x===""||x.startsWith("..")?c.patch.file:x});if(w.length===0){a.error("No applied files to commit. Aborting."),process.exitCode=1;return}const E=[`fix: vis ai heal accepted by @${n.actor}`,"",`Failing task: ${m.failedTask.taskId}`,f.proposal?.explanation?`
9
+ ${f.proposal.explanation}`:"","","Auto-committed by `vis ai heal accept`."].join(`
10
+ `);let v=r;if(r.provider==="buildkite"){const c=pe(r,d);if(!c){a.error("Cannot determine the upstream VCS to commit to. Buildkite jobs need GITHUB_TOKEN or GITLAB_TOKEN set (matching the host parsed from BUILDKITE_REPO) for `vis ai heal accept` to land the commit."),process.exitCode=1;return}v=c}let _;try{_=await ne({branch:p,ciContext:v,files:w,message:E,workspaceRoot:l,...t.commitOverrides})}catch(c){a.error(`Commit failed: ${c instanceof Error?c.message:String(c)}`),process.exitCode=1;return}a.success(`Committed as ${_.sha.slice(0,7)}${_.url?` — ${_.url}`:""}.`);const N=fe(_,w,m.failedTask.taskId,n.actor),R=await(t.postComment??(async(c,$)=>V({body:c,context:$})))(N,r);!R.posted&&R.method!=="skipped"&&a.warn(`Commit landed but the confirmation comment failed: ${R.error??"unknown"}`)},"acceptHeal"),Ie=g(async e=>{await he(e)},"aiHealAccept");export{G as TRIGGER_PHRASE,Ie as aiHealAccept,pe as deriveBuildkiteCommitContextForTesting,me as fetchGithubHeadRefForTesting,de as loadBuildkiteTriggerForTesting,ce as loadGithubTriggerForTesting,le as loadGitlabTriggerForTesting,he as runHealAcceptForTesting,fe as summariseDetailForTesting};