@ipul-labs/forge 1.16.8 → 1.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/README.md +39 -21
  2. package/lib/bin/cli/analyze.d.ts +9 -0
  3. package/lib/bin/cli/analyze.d.ts.map +1 -0
  4. package/lib/bin/cli/analyze.js +491 -0
  5. package/lib/bin/cli/analyze.js.map +1 -0
  6. package/lib/bin/cli/apply.d.ts +2 -0
  7. package/lib/bin/cli/apply.d.ts.map +1 -0
  8. package/lib/bin/cli/apply.js +262 -0
  9. package/lib/bin/cli/apply.js.map +1 -0
  10. package/lib/bin/cli/cifix.d.ts +2 -0
  11. package/lib/bin/cli/cifix.d.ts.map +1 -0
  12. package/lib/bin/cli/cifix.js +310 -0
  13. package/lib/bin/cli/cifix.js.map +1 -0
  14. package/lib/bin/cli/close.d.ts +2 -0
  15. package/lib/bin/cli/close.d.ts.map +1 -0
  16. package/lib/bin/cli/close.js +113 -0
  17. package/lib/bin/cli/close.js.map +1 -0
  18. package/lib/bin/cli/createIssue.d.ts +2 -0
  19. package/lib/bin/cli/createIssue.d.ts.map +1 -0
  20. package/lib/bin/cli/createIssue.js +108 -0
  21. package/lib/bin/cli/createIssue.js.map +1 -0
  22. package/lib/bin/cli/doctor.d.ts +2 -0
  23. package/lib/bin/cli/doctor.d.ts.map +1 -0
  24. package/lib/bin/cli/doctor.js +145 -0
  25. package/lib/bin/cli/doctor.js.map +1 -0
  26. package/lib/bin/cli/init.d.ts +4 -0
  27. package/lib/bin/cli/init.d.ts.map +1 -0
  28. package/lib/bin/cli/init.js +288 -0
  29. package/lib/bin/cli/init.js.map +1 -0
  30. package/lib/bin/cli/ink.d.ts +23 -0
  31. package/lib/bin/cli/ink.d.ts.map +1 -0
  32. package/{bin → lib/bin}/cli/ink.js +18 -19
  33. package/lib/bin/cli/ink.js.map +1 -0
  34. package/lib/bin/cli/issue.d.ts +2 -0
  35. package/lib/bin/cli/issue.d.ts.map +1 -0
  36. package/lib/bin/cli/issue.js +114 -0
  37. package/lib/bin/cli/issue.js.map +1 -0
  38. package/lib/bin/cli/issues.d.ts +2 -0
  39. package/lib/bin/cli/issues.d.ts.map +1 -0
  40. package/lib/bin/cli/issues.js +149 -0
  41. package/lib/bin/cli/issues.js.map +1 -0
  42. package/lib/bin/cli/merge.d.ts +2 -0
  43. package/lib/bin/cli/merge.d.ts.map +1 -0
  44. package/lib/bin/cli/merge.js +162 -0
  45. package/lib/bin/cli/merge.js.map +1 -0
  46. package/lib/bin/cli/refine.d.ts +2 -0
  47. package/lib/bin/cli/refine.d.ts.map +1 -0
  48. package/lib/bin/cli/refine.js +249 -0
  49. package/lib/bin/cli/refine.js.map +1 -0
  50. package/lib/bin/cli/repl.d.ts +2 -0
  51. package/lib/bin/cli/repl.d.ts.map +1 -0
  52. package/lib/bin/cli/repl.js +309 -0
  53. package/lib/bin/cli/repl.js.map +1 -0
  54. package/lib/bin/cli/repos.d.ts +2 -0
  55. package/lib/bin/cli/repos.d.ts.map +1 -0
  56. package/lib/bin/cli/repos.js +86 -0
  57. package/lib/bin/cli/repos.js.map +1 -0
  58. package/lib/bin/cli/resolution.d.ts +2 -0
  59. package/lib/bin/cli/resolution.d.ts.map +1 -0
  60. package/lib/bin/cli/resolution.js +139 -0
  61. package/lib/bin/cli/resolution.js.map +1 -0
  62. package/lib/bin/cli/serverClient.d.ts +26 -0
  63. package/lib/bin/cli/serverClient.d.ts.map +1 -0
  64. package/lib/bin/cli/serverClient.js +111 -0
  65. package/lib/bin/cli/serverClient.js.map +1 -0
  66. package/lib/bin/cli/session.d.ts +8 -0
  67. package/lib/bin/cli/session.d.ts.map +1 -0
  68. package/lib/bin/cli/session.js +212 -0
  69. package/lib/bin/cli/session.js.map +1 -0
  70. package/lib/bin/cli/sessionStore.d.ts +69 -0
  71. package/lib/bin/cli/sessionStore.d.ts.map +1 -0
  72. package/lib/bin/cli/sessionStore.js +196 -0
  73. package/lib/bin/cli/sessionStore.js.map +1 -0
  74. package/lib/bin/cli/ship.d.ts +33 -0
  75. package/lib/bin/cli/ship.d.ts.map +1 -0
  76. package/lib/bin/cli/ship.js +737 -0
  77. package/lib/bin/cli/ship.js.map +1 -0
  78. package/lib/bin/cli/types.d.ts +59 -0
  79. package/lib/bin/cli/types.d.ts.map +1 -0
  80. package/lib/bin/cli/types.js +2 -0
  81. package/lib/bin/cli/types.js.map +1 -0
  82. package/lib/bin/cli/update.d.ts +2 -0
  83. package/lib/bin/cli/update.d.ts.map +1 -0
  84. package/lib/bin/cli/update.js +87 -0
  85. package/lib/bin/cli/update.js.map +1 -0
  86. package/lib/bin/cli/utils.d.ts +43 -0
  87. package/lib/bin/cli/utils.d.ts.map +1 -0
  88. package/lib/bin/cli/utils.js +171 -0
  89. package/lib/bin/cli/utils.js.map +1 -0
  90. package/lib/bin/cli/validate.d.ts +2 -0
  91. package/lib/bin/cli/validate.d.ts.map +1 -0
  92. package/lib/bin/cli/validate.js +283 -0
  93. package/lib/bin/cli/validate.js.map +1 -0
  94. package/lib/bin/cli/watch.d.ts +5 -0
  95. package/lib/bin/cli/watch.d.ts.map +1 -0
  96. package/lib/bin/cli/watch.js +263 -0
  97. package/lib/bin/cli/watch.js.map +1 -0
  98. package/lib/bin/forge-agent.d.ts +3 -0
  99. package/lib/bin/forge-agent.d.ts.map +1 -0
  100. package/{bin → lib/bin}/forge-agent.js +104 -105
  101. package/lib/bin/forge-agent.js.map +1 -0
  102. package/lib/server/forge/core/ForgeAgent.d.ts +260 -0
  103. package/lib/server/forge/core/ForgeAgent.d.ts.map +1 -0
  104. package/lib/server/forge/core/ForgeAgent.js +995 -0
  105. package/lib/server/forge/core/ForgeAgent.js.map +1 -0
  106. package/lib/server/forge/core/agentClient.d.ts +39 -0
  107. package/lib/server/forge/core/agentClient.d.ts.map +1 -0
  108. package/{server → lib/server}/forge/core/agentClient.js +7 -12
  109. package/lib/server/forge/core/agentClient.js.map +1 -0
  110. package/lib/server/forge/core/branchName.d.ts +20 -0
  111. package/lib/server/forge/core/branchName.d.ts.map +1 -0
  112. package/lib/server/forge/core/branchName.js +39 -0
  113. package/lib/server/forge/core/branchName.js.map +1 -0
  114. package/lib/server/forge/core/claudeClient.d.ts +60 -0
  115. package/lib/server/forge/core/claudeClient.d.ts.map +1 -0
  116. package/lib/server/forge/core/claudeClient.js +347 -0
  117. package/lib/server/forge/core/claudeClient.js.map +1 -0
  118. package/lib/server/forge/core/configStore.d.ts +76 -0
  119. package/lib/server/forge/core/configStore.d.ts.map +1 -0
  120. package/lib/server/forge/core/configStore.js +213 -0
  121. package/lib/server/forge/core/configStore.js.map +1 -0
  122. package/lib/server/forge/core/gitlabClient.d.ts +144 -0
  123. package/lib/server/forge/core/gitlabClient.d.ts.map +1 -0
  124. package/{server → lib/server}/forge/core/gitlabClient.js +98 -180
  125. package/lib/server/forge/core/gitlabClient.js.map +1 -0
  126. package/lib/server/forge/core/jobs.d.ts +67 -0
  127. package/lib/server/forge/core/jobs.d.ts.map +1 -0
  128. package/lib/server/forge/core/jobs.js +185 -0
  129. package/lib/server/forge/core/jobs.js.map +1 -0
  130. package/lib/server/forge/core/k8s.d.ts +18 -0
  131. package/lib/server/forge/core/k8s.d.ts.map +1 -0
  132. package/lib/server/forge/core/k8s.js +49 -0
  133. package/lib/server/forge/core/k8s.js.map +1 -0
  134. package/lib/server/forge/core/localRepo.d.ts +36 -0
  135. package/lib/server/forge/core/localRepo.d.ts.map +1 -0
  136. package/lib/server/forge/core/localRepo.js +165 -0
  137. package/lib/server/forge/core/localRepo.js.map +1 -0
  138. package/lib/server/forge/core/openclawClient.d.ts +61 -0
  139. package/lib/server/forge/core/openclawClient.d.ts.map +1 -0
  140. package/lib/server/forge/core/openclawClient.js +111 -0
  141. package/lib/server/forge/core/openclawClient.js.map +1 -0
  142. package/lib/server/forge/core/prompts.d.ts +96 -0
  143. package/lib/server/forge/core/prompts.d.ts.map +1 -0
  144. package/lib/server/forge/core/prompts.js +450 -0
  145. package/lib/server/forge/core/prompts.js.map +1 -0
  146. package/lib/server/forge/core/providers/azuredevops.d.ts +134 -0
  147. package/lib/server/forge/core/providers/azuredevops.d.ts.map +1 -0
  148. package/lib/server/forge/core/providers/azuredevops.js +360 -0
  149. package/lib/server/forge/core/providers/azuredevops.js.map +1 -0
  150. package/lib/server/forge/core/providers/base.d.ts +114 -0
  151. package/lib/server/forge/core/providers/base.d.ts.map +1 -0
  152. package/lib/server/forge/core/providers/base.js +98 -0
  153. package/lib/server/forge/core/providers/base.js.map +1 -0
  154. package/lib/server/forge/core/providers/github.d.ts +133 -0
  155. package/lib/server/forge/core/providers/github.d.ts.map +1 -0
  156. package/lib/server/forge/core/providers/github.js +307 -0
  157. package/lib/server/forge/core/providers/github.js.map +1 -0
  158. package/lib/server/forge/core/providers/gitlab.d.ts +144 -0
  159. package/lib/server/forge/core/providers/gitlab.d.ts.map +1 -0
  160. package/lib/server/forge/core/providers/gitlab.js +407 -0
  161. package/lib/server/forge/core/providers/gitlab.js.map +1 -0
  162. package/lib/server/forge/core/providers/index.d.ts +21 -0
  163. package/lib/server/forge/core/providers/index.d.ts.map +1 -0
  164. package/lib/server/forge/core/providers/index.js +43 -0
  165. package/lib/server/forge/core/providers/index.js.map +1 -0
  166. package/lib/server/forge/core/providers/types.d.ts +80 -0
  167. package/lib/server/forge/core/providers/types.d.ts.map +1 -0
  168. package/lib/server/forge/core/providers/types.js +2 -0
  169. package/lib/server/forge/core/providers/types.js.map +1 -0
  170. package/lib/server/forge/core/quotaClient.d.ts +48 -0
  171. package/lib/server/forge/core/quotaClient.d.ts.map +1 -0
  172. package/lib/server/forge/core/quotaClient.js +88 -0
  173. package/lib/server/forge/core/quotaClient.js.map +1 -0
  174. package/lib/server/forge/core/types.d.ts +160 -0
  175. package/lib/server/forge/core/types.d.ts.map +1 -0
  176. package/lib/server/forge/core/types.js +2 -0
  177. package/lib/server/forge/core/types.js.map +1 -0
  178. package/lib/server/forge/core/validator/attribute.d.ts +31 -0
  179. package/lib/server/forge/core/validator/attribute.d.ts.map +1 -0
  180. package/lib/server/forge/core/validator/attribute.js +82 -0
  181. package/lib/server/forge/core/validator/attribute.js.map +1 -0
  182. package/lib/server/forge/core/validator/clone.d.ts +14 -0
  183. package/lib/server/forge/core/validator/clone.d.ts.map +1 -0
  184. package/lib/server/forge/core/validator/clone.js +99 -0
  185. package/lib/server/forge/core/validator/clone.js.map +1 -0
  186. package/lib/server/forge/core/validator/cmd.d.ts +23 -0
  187. package/lib/server/forge/core/validator/cmd.d.ts.map +1 -0
  188. package/lib/server/forge/core/validator/cmd.js +87 -0
  189. package/lib/server/forge/core/validator/cmd.js.map +1 -0
  190. package/lib/server/forge/core/validator/fs.d.ts +5 -0
  191. package/lib/server/forge/core/validator/fs.d.ts.map +1 -0
  192. package/{server → lib/server}/forge/core/validator/fs.js +8 -3
  193. package/lib/server/forge/core/validator/fs.js.map +1 -0
  194. package/lib/server/forge/core/validator/overlay.d.ts +13 -0
  195. package/lib/server/forge/core/validator/overlay.d.ts.map +1 -0
  196. package/lib/server/forge/core/validator/overlay.js +53 -0
  197. package/lib/server/forge/core/validator/overlay.js.map +1 -0
  198. package/lib/server/forge/core/validator/projectType.d.ts +20 -0
  199. package/lib/server/forge/core/validator/projectType.d.ts.map +1 -0
  200. package/lib/server/forge/core/validator/projectType.js +94 -0
  201. package/lib/server/forge/core/validator/projectType.js.map +1 -0
  202. package/lib/server/forge/core/validator/runners/go.d.ts +21 -0
  203. package/lib/server/forge/core/validator/runners/go.d.ts.map +1 -0
  204. package/lib/server/forge/core/validator/runners/go.js +99 -0
  205. package/lib/server/forge/core/validator/runners/go.js.map +1 -0
  206. package/lib/server/forge/core/validator/runners/node.d.ts +12 -0
  207. package/lib/server/forge/core/validator/runners/node.d.ts.map +1 -0
  208. package/lib/server/forge/core/validator/runners/node.js +75 -0
  209. package/lib/server/forge/core/validator/runners/node.js.map +1 -0
  210. package/lib/server/forge/core/validator/tdd.d.ts +30 -0
  211. package/lib/server/forge/core/validator/tdd.d.ts.map +1 -0
  212. package/lib/server/forge/core/validator/tdd.js +96 -0
  213. package/lib/server/forge/core/validator/tdd.js.map +1 -0
  214. package/lib/server/forge/core/validator.d.ts +58 -0
  215. package/lib/server/forge/core/validator.d.ts.map +1 -0
  216. package/lib/server/forge/core/validator.js +150 -0
  217. package/lib/server/forge/core/validator.js.map +1 -0
  218. package/lib/server/forge/routes.d.ts +22 -0
  219. package/lib/server/forge/routes.d.ts.map +1 -0
  220. package/lib/server/forge/routes.js +984 -0
  221. package/lib/server/forge/routes.js.map +1 -0
  222. package/lib/server.d.ts +2 -0
  223. package/lib/server.d.ts.map +1 -0
  224. package/lib/server.js +52 -0
  225. package/lib/server.js.map +1 -0
  226. package/lib/shared/events.d.ts +12 -0
  227. package/lib/shared/events.d.ts.map +1 -0
  228. package/lib/shared/events.js +12 -0
  229. package/lib/shared/events.js.map +1 -0
  230. package/package.json +15 -13
  231. package/scripts/fix-shebang.cjs +10 -0
  232. package/bin/cli/analyze.js +0 -516
  233. package/bin/cli/apply.js +0 -264
  234. package/bin/cli/cifix.js +0 -300
  235. package/bin/cli/close.js +0 -125
  236. package/bin/cli/createIssue.js +0 -112
  237. package/bin/cli/doctor.js +0 -161
  238. package/bin/cli/init.js +0 -306
  239. package/bin/cli/issue.js +0 -116
  240. package/bin/cli/issues.js +0 -155
  241. package/bin/cli/merge.js +0 -175
  242. package/bin/cli/refine.js +0 -253
  243. package/bin/cli/repl.js +0 -301
  244. package/bin/cli/repos.js +0 -91
  245. package/bin/cli/resolution.js +0 -126
  246. package/bin/cli/session.js +0 -230
  247. package/bin/cli/sessionStore.js +0 -211
  248. package/bin/cli/ship.js +0 -762
  249. package/bin/cli/update.js +0 -95
  250. package/bin/cli/utils.js +0 -145
  251. package/bin/cli/validate.js +0 -280
  252. package/bin/cli/watch.js +0 -274
  253. package/server/forge/core/ForgeAgent.js +0 -1179
  254. package/server/forge/core/ForgeAgent.test.js +0 -179
  255. package/server/forge/core/branchName.js +0 -41
  256. package/server/forge/core/claudeClient.js +0 -378
  257. package/server/forge/core/configStore.js +0 -229
  258. package/server/forge/core/configStore.test.js +0 -89
  259. package/server/forge/core/jobs.js +0 -180
  260. package/server/forge/core/k8s.js +0 -54
  261. package/server/forge/core/localRepo.js +0 -163
  262. package/server/forge/core/openclawClient.js +0 -125
  263. package/server/forge/core/prompts.js +0 -483
  264. package/server/forge/core/prompts.test.js +0 -219
  265. package/server/forge/core/providers/azuredevops.js +0 -409
  266. package/server/forge/core/providers/base.js +0 -208
  267. package/server/forge/core/providers/github.js +0 -348
  268. package/server/forge/core/providers/gitlab.js +0 -529
  269. package/server/forge/core/providers/index.js +0 -47
  270. package/server/forge/core/quotaClient.js +0 -101
  271. package/server/forge/core/validator/attribute.js +0 -84
  272. package/server/forge/core/validator/clone.js +0 -116
  273. package/server/forge/core/validator/cmd.js +0 -98
  274. package/server/forge/core/validator/overlay.js +0 -52
  275. package/server/forge/core/validator/projectType.js +0 -87
  276. package/server/forge/core/validator/runners/go.js +0 -107
  277. package/server/forge/core/validator/runners/node.js +0 -84
  278. package/server/forge/core/validator/tdd.js +0 -105
  279. package/server/forge/core/validator.js +0 -167
  280. package/server/forge/routes.js +0 -1027
package/bin/cli/issues.js DELETED
@@ -1,155 +0,0 @@
1
- import path from "node:path";
2
- import React, { useEffect, useState } from "react";
3
- import Spinner from "ink-spinner";
4
- import { h, Box, Text, useApp, runApp } from "./ink.js";
5
- import { projectRoot, getFlag, relativeAge, resolvePrimaryRepo } from "./utils.js";
6
-
7
- function applyFilters(list, { label, mine, currentUser }) {
8
- let out = list;
9
- if (label) {
10
- const want = label.toLowerCase();
11
- out = out.filter((i) => (i.labels ?? []).some((l) => l.toLowerCase() === want));
12
- }
13
- if (mine && currentUser) {
14
- const want = currentUser.toLowerCase();
15
- out = out.filter((i) => (i.author?.name ?? "").toLowerCase() === want);
16
- }
17
- return out;
18
- }
19
-
20
- function IssuesView({ primary, limit, label, mine, onError }) {
21
- const { exit } = useApp();
22
- const [phase, setPhase] = useState("loading");
23
- const [issues, setIssues] = useState([]);
24
- const [error, setError] = useState(null);
25
-
26
- useEffect(() => {
27
- let cancelled = false;
28
- (async () => {
29
- try {
30
- const { createProvider } = await import(
31
- path.join(projectRoot, "server/forge/core/providers/index.js")
32
- );
33
- const provider = createProvider(primary);
34
- let list = await provider.listOpenIssues();
35
- if (cancelled) return;
36
- list = applyFilters(list, {
37
- label,
38
- mine,
39
- currentUser: process.env.FORGE_USER ?? process.env.USER ?? null,
40
- });
41
- if (Number.isInteger(limit) && limit > 0) list = list.slice(0, limit);
42
- setIssues(list);
43
- setPhase("done");
44
- } catch (err) {
45
- if (cancelled) return;
46
- setError(err.message || String(err));
47
- setPhase("error");
48
- onError(err);
49
- }
50
- })();
51
- return () => {
52
- cancelled = true;
53
- };
54
- }, []);
55
-
56
- useEffect(() => {
57
- if (phase === "done" || phase === "error") exit();
58
- }, [phase]);
59
-
60
- if (phase === "loading") {
61
- return h`<${Box}>
62
- <${Text} color="cyan"><${Spinner} type="dots"/></>
63
- <${Text}> Fetching issues from </>
64
- <${Text} dimColor>${primary}</>
65
- </>`;
66
- }
67
-
68
- if (phase === "error") {
69
- return h`<${Box}>
70
- <${Text} color="red">✘</>
71
- <${Text}> Failed to fetch issues: ${error}</>
72
- </>`;
73
- }
74
-
75
- const count = issues.length;
76
- const header = h`<${Box} marginBottom=${1}>
77
- <${Text} color="green">✓</>
78
- <${Text}> ${count} open issue${count === 1 ? "" : "s"} in </>
79
- <${Text} dimColor>${primary}</>
80
- </>`;
81
-
82
- if (count === 0) return header;
83
-
84
- const rows = issues.map((i) => ({
85
- age: relativeAge(i.created_at),
86
- iid: `#${i.iid}`,
87
- title: i.title.length > 60 ? `${i.title.slice(0, 57)}…` : i.title,
88
- labels: (i.labels ?? []).slice(0, 3).join(","),
89
- }));
90
- const wAge = Math.max(3, ...rows.map((r) => r.age.length));
91
- const wIid = Math.max(3, ...rows.map((r) => r.iid.length));
92
-
93
- return h`<${Box} flexDirection="column">
94
- ${header}
95
- ${rows.map((r, i) =>
96
- h`<${Box} key=${i}>
97
- <${Box} width=${wAge + 2}><${Text} dimColor>${r.age}</></>
98
- <${Box} width=${wIid + 2}><${Text} color="yellow">${r.iid}</></>
99
- <${Box} flexGrow=${1}><${Text}>${r.title}</></>
100
- <${Text} dimColor> ${r.labels}</>
101
- </>`,
102
- )}
103
- <${Box} marginTop=${1}>
104
- <${Text} dimColor>${"Analyze: forge analyze <iid>"}</>
105
- </>
106
- </>`;
107
- }
108
-
109
- export async function cmdIssues(argv) {
110
- const primary = await resolvePrimaryRepo(argv);
111
- if (!primary) {
112
- console.error(
113
- "No repo. Pass --repo <group/repo>, set FORGE_GITLAB_PROJECT, or run `forge init`.",
114
- );
115
- process.exit(1);
116
- }
117
-
118
- const json = argv.includes("--json");
119
- const limitStr = getFlag(argv, "--limit");
120
- const limit = limitStr ? parseInt(limitStr, 10) : null;
121
- const label = getFlag(argv, "--label");
122
- const mine = argv.includes("--mine");
123
-
124
- if (json) {
125
- const { createProvider } = await import(
126
- path.join(projectRoot, "server/forge/core/providers/index.js")
127
- );
128
- const provider = createProvider(primary);
129
- try {
130
- let issues = await provider.listOpenIssues();
131
- issues = applyFilters(issues, {
132
- label,
133
- mine,
134
- currentUser: process.env.FORGE_USER ?? process.env.USER ?? null,
135
- });
136
- if (Number.isInteger(limit) && limit > 0) issues = issues.slice(0, limit);
137
- process.stdout.write(`${JSON.stringify(issues, null, 2)}\n`);
138
- } catch (err) {
139
- process.stdout.write(`${JSON.stringify({ error: err.message })}\n`);
140
- process.exit(2);
141
- }
142
- return;
143
- }
144
-
145
- let failed = false;
146
- await runApp(
147
- h`<${IssuesView}
148
- primary=${primary}
149
- limit=${limit}
150
- label=${label}
151
- mine=${mine}
152
- onError=${() => (failed = true)} />`,
153
- );
154
- if (failed) process.exit(2);
155
- }
package/bin/cli/merge.js DELETED
@@ -1,175 +0,0 @@
1
- import path from "node:path";
2
- import React, { useEffect, useState } from "react";
3
- import Spinner from "ink-spinner";
4
- import { h, Box, Text, useApp, runApp, Confirm } from "./ink.js";
5
- import { getFlag, projectRoot } from "./utils.js";
6
- import {
7
- PHASES,
8
- appendEvent,
9
- readState,
10
- resolveSessionTarget,
11
- updateState,
12
- } from "./sessionStore.js";
13
-
14
- function MergeView({ repo, mrIid, autoMerge, bypassYes, onDone }) {
15
- const { exit } = useApp();
16
- const [phase, setPhase] = useState("review"); // review → merging → done | aborted | error
17
- const [error, setError] = useState(null);
18
-
19
- useEffect(() => {
20
- if (phase === "done" || phase === "aborted" || phase === "error") {
21
- onDone({ phase, error });
22
- exit();
23
- }
24
- }, [phase]);
25
-
26
- useEffect(() => {
27
- if (phase !== "merging") return;
28
- let cancelled = false;
29
- (async () => {
30
- try {
31
- const { createProvider } = await import(
32
- path.join(projectRoot, "server/forge/core/providers/index.js")
33
- );
34
- const provider = createProvider(repo);
35
- if (autoMerge) {
36
- if (typeof provider.setAutoMergeMR !== "function") {
37
- throw new Error(
38
- `${provider.name ?? "provider"} does not support arming auto-merge.`,
39
- );
40
- }
41
- await provider.setAutoMergeMR(mrIid);
42
- } else {
43
- await provider.mergeMR(mrIid);
44
- }
45
- if (cancelled) return;
46
- setPhase("done");
47
- } catch (err) {
48
- if (cancelled) return;
49
- setError(err);
50
- setPhase("error");
51
- }
52
- })();
53
- return () => {
54
- cancelled = true;
55
- };
56
- }, [phase]);
57
-
58
- const action = autoMerge
59
- ? "Arm auto-merge for this MR (will merge once CI is green)?"
60
- : "Merge this MR now?";
61
-
62
- if (phase === "review" || phase === "confirm") {
63
- return h`<${Box} flexDirection="column">
64
- <${Box}>
65
- <${Text} color="yellow">MR </>
66
- <${Text}>${repo} !${mrIid}</>
67
- </>
68
- <${Confirm}
69
- message=${action}
70
- bypassYes=${bypassYes}
71
- onSelect=${(yes) => setPhase(yes ? "merging" : "aborted")} />
72
- </>`;
73
- }
74
-
75
- if (phase === "merging") {
76
- return h`<${Box}>
77
- <${Text} color="cyan"><${Spinner} type="dots"/></>
78
- <${Text}>${autoMerge ? " Arming auto-merge…" : " Merging…"}</>
79
- </>`;
80
- }
81
-
82
- if (phase === "done") {
83
- return h`<${Box}>
84
- <${Text} color="green">${autoMerge ? "✓ Auto-merge armed." : "✓ Merged."}</>
85
- </>`;
86
- }
87
-
88
- if (phase === "aborted") {
89
- return h`<${Text} dimColor>Aborted — no merge.</>`;
90
- }
91
-
92
- return h`<${Box}>
93
- <${Text} color="red">${`✘ ${error?.message ?? String(error)}`}</>
94
- </>`;
95
- }
96
-
97
- export async function cmdMerge(argv) {
98
- const explicitIid = Number(argv[1]);
99
- const explicitRepo = getFlag(argv, "--repo");
100
- const explicitMr = Number(getFlag(argv, "--mr"));
101
- const autoMerge = argv.includes("--auto");
102
- const bypassYes = argv.includes("--yes") || process.env.FORGE_YES === "1";
103
-
104
- let repo;
105
- let mrIid;
106
- let sessionRef = null;
107
-
108
- if (Number.isInteger(explicitMr) && explicitMr > 0) {
109
- repo = explicitRepo;
110
- mrIid = explicitMr;
111
- } else {
112
- const target = await resolveSessionTarget({
113
- explicitIid:
114
- Number.isInteger(explicitIid) && explicitIid > 0 ? explicitIid : null,
115
- explicitRepo,
116
- });
117
- if (!target?.repo || !target?.iid) {
118
- console.error(
119
- "No session target. Pass --mr <iid> --repo <repo>, or run analyze + apply first.",
120
- );
121
- process.exit(1);
122
- }
123
- const state = await readState(target.repo, target.iid);
124
- if (!state?.mrIid) {
125
- console.error(
126
- `Session ${target.repo}#${target.iid} has no MR. Run: forge apply ${target.iid}`,
127
- );
128
- process.exit(1);
129
- }
130
- repo = state.primary ?? target.repo;
131
- mrIid = state.mrIid;
132
- sessionRef = target;
133
- }
134
-
135
- if (!repo) {
136
- console.error("Repo is required: pass --repo <id>");
137
- process.exit(1);
138
- }
139
-
140
- let outcome = { phase: null };
141
- await runApp(
142
- h`<${MergeView}
143
- repo=${repo}
144
- mrIid=${mrIid}
145
- autoMerge=${autoMerge}
146
- bypassYes=${bypassYes}
147
- onDone=${(o) => (outcome = o)} />`,
148
- );
149
-
150
- if (sessionRef) {
151
- if (outcome.phase === "done") {
152
- await updateState(sessionRef.repo, sessionRef.iid, {
153
- phase: PHASES.MERGED,
154
- mergedAt: new Date().toISOString(),
155
- autoMergeArmed: !!autoMerge,
156
- lastError: null,
157
- }).catch(() => {});
158
- await appendEvent(sessionRef.repo, sessionRef.iid, {
159
- type: "session:merge-done",
160
- autoMerge: !!autoMerge,
161
- }).catch(() => {});
162
- } else if (outcome.phase === "error") {
163
- await updateState(sessionRef.repo, sessionRef.iid, {
164
- lastError: outcome.error?.message ?? null,
165
- }).catch(() => {});
166
- await appendEvent(sessionRef.repo, sessionRef.iid, {
167
- type: "session:merge-failed",
168
- error: outcome.error?.message,
169
- }).catch(() => {});
170
- }
171
- }
172
-
173
- if (outcome.phase === "error") process.exit(2);
174
- if (outcome.phase === "aborted") process.exit(0);
175
- }
package/bin/cli/refine.js DELETED
@@ -1,253 +0,0 @@
1
- import path from "node:path";
2
- import React, { useEffect, useState } from "react";
3
- import Spinner from "ink-spinner";
4
- import TextInput from "ink-text-input";
5
- import { h, Box, Text, useApp, useInput, runApp } from "./ink.js";
6
- import { getFlag, projectRoot } from "./utils.js";
7
- import {
8
- PHASES,
9
- appendEvent,
10
- readAnalysis,
11
- readState,
12
- resolveSessionTarget,
13
- updateState,
14
- writeAnalysis,
15
- } from "./sessionStore.js";
16
-
17
- function StepRow({ step, message, status }) {
18
- let glyph;
19
- if (status === "running")
20
- glyph = h`<${Text} color="cyan"><${Spinner} type="dots"/></>`;
21
- else if (status === "done") glyph = h`<${Text} color="green">✓</>`;
22
- else glyph = h`<${Text} color="red">✘</>`;
23
- return h`<${Box}>
24
- ${glyph}
25
- <${Text} dimColor> [${step}]</>
26
- <${Text}> ${message}</>
27
- </>`;
28
- }
29
-
30
- function FeedbackPrompt({ onSubmit }) {
31
- const { exit } = useApp();
32
- const [input, setInput] = useState("");
33
- return h`<${Box} flexDirection="column">
34
- <${Box}>
35
- <${Text} color="cyan">? </>
36
- <${Text}>What feedback should the model address?</>
37
- </>
38
- <${Box} marginLeft=${2}>
39
- <${TextInput}
40
- value=${input}
41
- onChange=${setInput}
42
- onSubmit=${(v) => {
43
- const trimmed = v.trim();
44
- if (!trimmed) return; // require non-empty
45
- onSubmit(trimmed);
46
- exit();
47
- }} />
48
- </>
49
- </>`;
50
- }
51
-
52
- function RefineView({ analysis, primary, secondaries, feedback, model, eventSink, onDone }) {
53
- const { exit } = useApp();
54
- const [items, setItems] = useState([]);
55
- const [result, setResult] = useState(null);
56
- const [finished, setFinished] = useState(null);
57
-
58
- useEffect(() => {
59
- if (!finished) return;
60
- onDone(finished);
61
- exit();
62
- }, [finished]);
63
-
64
- useEffect(() => {
65
- let cancelled = false;
66
- (async () => {
67
- try {
68
- const { ForgeAgent } = await import(
69
- path.join(projectRoot, "server/forge/core/ForgeAgent.js")
70
- );
71
- const agent = new ForgeAgent({
72
- project: primary,
73
- secondaryProjects: secondaries,
74
- });
75
- let final = null;
76
-
77
- const onEvent = (evt) => {
78
- if (cancelled) return;
79
- eventSink?.(evt);
80
- if (evt.type === "result") {
81
- final = evt.data;
82
- return;
83
- }
84
- setItems((prev) => {
85
- const closeRunning = (list) =>
86
- list.map((it) =>
87
- it.kind === "step" && it.status === "running"
88
- ? { ...it, status: "done" }
89
- : it,
90
- );
91
- switch (evt.type) {
92
- case "progress":
93
- return [
94
- ...closeRunning(prev),
95
- {
96
- kind: "step",
97
- step: evt.step,
98
- message: evt.message,
99
- status: "running",
100
- },
101
- ];
102
- case "log":
103
- return [...prev, { kind: "log", text: evt.message }];
104
- case "error":
105
- return [
106
- ...prev.map((it) =>
107
- it.kind === "step" && it.status === "running"
108
- ? { ...it, status: "failed" }
109
- : it,
110
- ),
111
- { kind: "error", message: evt.message },
112
- ];
113
- default:
114
- return prev;
115
- }
116
- });
117
- };
118
-
119
- await agent.refineIssue(
120
- analysis.issue.iid,
121
- analysis.changes,
122
- feedback,
123
- onEvent,
124
- model,
125
- undefined, // ciContext
126
- undefined, // validationState
127
- undefined, // claudeSessionId — TODO: persist in session for multi-turn
128
- );
129
- if (cancelled) return;
130
- setItems((prev) =>
131
- prev.map((it) =>
132
- it.kind === "step" && it.status === "running"
133
- ? { ...it, status: "done" }
134
- : it,
135
- ),
136
- );
137
- if (final) setResult(final);
138
- setFinished({ ok: true, result: final });
139
- } catch (err) {
140
- if (cancelled) return;
141
- setItems((prev) => [
142
- ...prev.map((it) =>
143
- it.kind === "step" && it.status === "running"
144
- ? { ...it, status: "failed" }
145
- : it,
146
- ),
147
- { kind: "error", message: err.message ?? String(err) },
148
- ]);
149
- setFinished({ ok: false, error: err });
150
- }
151
- })();
152
- return () => {
153
- cancelled = true;
154
- };
155
- }, []);
156
-
157
- return h`<${Box} flexDirection="column">
158
- <${Box}>
159
- <${Text} dimColor>Feedback: </>
160
- <${Text}>${feedback}</>
161
- </>
162
- ${items.map((it, i) => {
163
- if (it.kind === "step")
164
- return h`<${StepRow} key=${i} step=${it.step} message=${it.message} status=${it.status} />`;
165
- if (it.kind === "log")
166
- return h`<${Box} key=${i}><${Text} dimColor> ${it.text}</></>`;
167
- return h`<${Box} key=${i}>
168
- <${Text} color="red">ERROR</>
169
- <${Text}>: ${it.message}</>
170
- </>`;
171
- })}
172
- ${result
173
- ? h`<${Box} marginTop=${1}>
174
- <${Text} color="green">${`✓ Refined ${result.changes?.length ?? 0} change(s) — view with: forge show`}</>
175
- </>`
176
- : null}
177
- </>`;
178
- }
179
-
180
- export async function cmdRefine(argv) {
181
- const explicitIid = Number(argv[1]);
182
- const explicitRepo = getFlag(argv, "--repo");
183
- const target = await resolveSessionTarget({
184
- explicitIid: Number.isInteger(explicitIid) && explicitIid > 0 ? explicitIid : null,
185
- explicitRepo,
186
- });
187
- if (!target?.repo || !target?.iid) {
188
- console.error(
189
- "No session target. Pass an <iid>, set --repo, or run analyze first.",
190
- );
191
- process.exit(1);
192
- }
193
- const analysis = await readAnalysis(target.repo, target.iid);
194
- if (!analysis) {
195
- console.error(
196
- `No cached analysis at ${target.repo}#${target.iid}. Run: forge analyze ${target.iid}`,
197
- );
198
- process.exit(1);
199
- }
200
- const state = (await readState(target.repo, target.iid)) ?? {};
201
- const primary = state.primary ?? target.repo;
202
- const secondaries = state.secondaries ?? [];
203
- const model = getFlag(argv, "--model") || undefined;
204
-
205
- let feedback = getFlag(argv, "--feedback");
206
- if (!feedback) {
207
- // Interactive prompt
208
- await runApp(
209
- h`<${FeedbackPrompt} onSubmit=${(v) => (feedback = v)} />`,
210
- );
211
- if (!feedback) {
212
- console.error("Feedback is required. Use --feedback \"...\" or enter it interactively.");
213
- process.exit(1);
214
- }
215
- }
216
-
217
- let outcome = { ok: false };
218
- const events = [];
219
- await runApp(
220
- h`<${RefineView}
221
- analysis=${analysis}
222
- primary=${primary}
223
- secondaries=${secondaries}
224
- feedback=${feedback}
225
- model=${model}
226
- eventSink=${(e) => events.push(e)}
227
- onDone=${(o) => (outcome = o)} />`,
228
- );
229
-
230
- if (outcome.ok && outcome.result) {
231
- await writeAnalysis(target.repo, target.iid, outcome.result);
232
- await updateState(target.repo, target.iid, {
233
- phase: PHASES.REFINED,
234
- lastError: null,
235
- lastFeedback: feedback,
236
- });
237
- await appendEvent(target.repo, target.iid, {
238
- type: "session:refine-done",
239
- feedback,
240
- });
241
- } else {
242
- await updateState(target.repo, target.iid, {
243
- phase: "refine-failed",
244
- lastError: outcome.error?.message ?? null,
245
- });
246
- await appendEvent(target.repo, target.iid, {
247
- type: "session:refine-failed",
248
- feedback,
249
- error: outcome.error?.message,
250
- });
251
- process.exit(2);
252
- }
253
- }