@ipul-labs/forge 1.16.8 → 1.17.1

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 +181 -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 +11 -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/update.js DELETED
@@ -1,95 +0,0 @@
1
- import { execSync } from "node:child_process";
2
- import path from "node:path";
3
- import React, { useEffect, useState } from "react";
4
- import Spinner from "ink-spinner";
5
- import { h, Box, Text, useApp, runApp } from "./ink.js";
6
- import { projectRoot, readPkg } from "./utils.js";
7
-
8
- function UpdateView({ onDone }) {
9
- const { exit } = useApp();
10
- const [phase, setPhase] = useState("checking");
11
- const [info, setInfo] = useState({});
12
- const [error, setError] = useState(null);
13
-
14
- useEffect(() => {
15
- if (phase === "done" || phase === "error") {
16
- onDone({ phase, info, error });
17
- exit();
18
- }
19
- }, [phase]);
20
-
21
- useEffect(() => {
22
- (async () => {
23
- try {
24
- const before = (await readPkg()).version;
25
- setInfo((prev) => ({ ...prev, before }));
26
-
27
- setPhase("pulling");
28
- execSync("git pull --rebase", { cwd: projectRoot, stdio: "pipe" });
29
-
30
- setPhase("installing");
31
- execSync("npm install --no-audit --no-fund", {
32
- cwd: projectRoot,
33
- stdio: "pipe",
34
- });
35
-
36
- const after = (await readPkg()).version;
37
- setInfo({ before, after, updated: before !== after });
38
- setPhase("done");
39
- } catch (err) {
40
- setError(err);
41
- setPhase("error");
42
- }
43
- })();
44
- }, []);
45
-
46
- if (phase === "checking") {
47
- return h`<${Box}>
48
- <${Text} color="cyan"><${Spinner} type="dots"/></>
49
- <${Text}> Checking for updates…</>
50
- </>`;
51
- }
52
- if (phase === "pulling") {
53
- return h`<${Box}>
54
- <${Text} color="cyan"><${Spinner} type="dots"/></>
55
- <${Text}> Pulling latest changes…</>
56
- </>`;
57
- }
58
- if (phase === "installing") {
59
- return h`<${Box}>
60
- <${Text} color="cyan"><${Spinner} type="dots"/></>
61
- <${Text}> Installing dependencies…</>
62
- </>`;
63
- }
64
- if (phase === "done") {
65
- if (info.updated) {
66
- return h`<${Box} flexDirection="column">
67
- <${Box}>
68
- <${Text} color="green">✓ </>
69
- <${Text}>Updated </>
70
- <${Text} dimColor>${info.before}</>
71
- <${Text}> → </>
72
- <${Text} color="green">${info.after}</>
73
- </>
74
- <${Box}><${Text} dimColor>Restart forge to use the new version.</>
75
- </>
76
- </>`;
77
- }
78
- return h`<${Box}>
79
- <${Text} color="green">✓ </>
80
- <${Text}>Already up to date </>
81
- <${Text} dimColor>(v${info.after})</>
82
- </>`;
83
- }
84
- return h`<${Box}>
85
- <${Text} color="red">${`✘ Update failed: ${error?.message ?? String(error)}`}</>
86
- </>`;
87
- }
88
-
89
- export async function cmdUpdate() {
90
- let outcome = { phase: null };
91
- await runApp(
92
- h`<${UpdateView} onDone=${(o) => (outcome = o)} />`,
93
- );
94
- if (outcome.phase === "error") process.exit(2);
95
- }
package/bin/cli/utils.js DELETED
@@ -1,145 +0,0 @@
1
- import fs from "node:fs/promises";
2
- import path from "node:path";
3
- import { fileURLToPath } from "node:url";
4
-
5
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
6
- export const projectRoot = path.dirname(path.dirname(__dirname));
7
-
8
- export async function readPkg() {
9
- const raw = await fs.readFile(path.join(projectRoot, "package.json"), "utf8");
10
- return JSON.parse(raw);
11
- }
12
-
13
- export function getFlag(argv, name) {
14
- const idx = argv.indexOf(name);
15
- if (idx !== -1 && argv[idx + 1] && !argv[idx + 1].startsWith("-")) return argv[idx + 1];
16
- const eq = argv.find((a) => a.startsWith(`${name}=`));
17
- return eq ? eq.slice(name.length + 1) : null;
18
- }
19
-
20
- /**
21
- * Collect every value of a repeatable flag: --secondary a --secondary b → ["a", "b"].
22
- */
23
- export function getFlagAll(argv, name) {
24
- const out = [];
25
- for (let i = 0; i < argv.length; i++) {
26
- if (argv[i] === name && argv[i + 1] && !argv[i + 1].startsWith("-")) {
27
- out.push(argv[i + 1]);
28
- i += 1;
29
- } else if (argv[i].startsWith(`${name}=`)) {
30
- out.push(argv[i].slice(name.length + 1));
31
- }
32
- }
33
- return out;
34
- }
35
-
36
- export function parsePort(argv) {
37
- let raw;
38
- const i = argv.indexOf("-p");
39
- const j = argv.indexOf("--port");
40
- if (i !== -1 && argv[i + 1]) raw = argv[i + 1];
41
- else if (j !== -1 && argv[j + 1]) raw = argv[j + 1];
42
- else {
43
- const eq = argv.find((a) => a.startsWith("--port="));
44
- if (eq) raw = eq.slice("--port=".length);
45
- }
46
- if (raw == null) return null;
47
- const n = Number(raw);
48
- if (!Number.isInteger(n) || n < 1 || n > 65535) {
49
- console.error(`Invalid port: ${raw} (must be 1–65535)`);
50
- process.exit(1);
51
- }
52
- return n;
53
- }
54
-
55
- export function relativeAge(iso) {
56
- if (!iso) return "";
57
- const ms = Date.now() - new Date(iso).getTime();
58
- if (Number.isNaN(ms) || ms < 0) return "";
59
- const s = Math.floor(ms / 1000);
60
- if (s < 60) return `${s}s`;
61
- const m = Math.floor(s / 60);
62
- if (m < 60) return `${m}m`;
63
- const h = Math.floor(m / 60);
64
- if (h < 24) return `${h}h`;
65
- const d = Math.floor(h / 24);
66
- if (d < 30) return `${d}d`;
67
- const mo = Math.floor(d / 30);
68
- if (mo < 12) return `${mo}mo`;
69
- return `${Math.floor(mo / 12)}y`;
70
- }
71
-
72
- export async function resolvePrimaryRepo(argv) {
73
- const repoFlag = getFlag(argv, "--repo");
74
- let primary = repoFlag || process.env.FORGE_GITLAB_PROJECT;
75
- if (!primary) {
76
- const { get } = await import(
77
- path.join(projectRoot, "server/forge/core/configStore.js")
78
- );
79
- primary = get().repos?.[0]?.id;
80
- }
81
- return primary;
82
- }
83
-
84
- /**
85
- * Strip a "gitlab:" / "github:" / "azuredevops:" prefix so two repo strings
86
- * that point at the same project compare equal regardless of where they came
87
- * from (env vars are usually unprefixed; configStore entries are prefixed).
88
- */
89
- export function canonicalRepoId(id) {
90
- if (!id) return id;
91
- return id.replace(/^(gitlab|github|azuredevops):/, "");
92
- }
93
-
94
- /**
95
- * Resolve { primary, secondaries } from argv. Honours:
96
- * - --repo <id> primary repo (overrides default)
97
- * - --secondary <id> additional repo (repeatable)
98
- * - --all-repos all configured repos except primary become secondaries
99
- *
100
- * Dedup is prefix-insensitive so an env-provided primary doesn't double up
101
- * with its configStore-prefixed twin.
102
- */
103
- export async function resolveRepoSet(argv) {
104
- const primary = await resolvePrimaryRepo(argv);
105
- const explicit = getFlagAll(argv, "--secondary");
106
- const allRepos = argv.includes("--all-repos");
107
-
108
- const primaryCanon = canonicalRepoId(primary);
109
- let secondaries = explicit.filter((id) => canonicalRepoId(id) !== primaryCanon);
110
-
111
- if (allRepos) {
112
- const { get } = await import(
113
- path.join(projectRoot, "server/forge/core/configStore.js")
114
- );
115
- const configured = (get().repos ?? []).map((r) => r.id);
116
- const fromConfig = configured.filter(
117
- (id) => canonicalRepoId(id) !== primaryCanon,
118
- );
119
- const merged = [...secondaries, ...fromConfig];
120
- // Dedup secondaries against each other (prefix-insensitive, keep first).
121
- const seen = new Set();
122
- secondaries = merged.filter((id) => {
123
- const key = canonicalRepoId(id);
124
- if (seen.has(key)) return false;
125
- seen.add(key);
126
- return true;
127
- });
128
- }
129
- return { primary, secondaries };
130
- }
131
-
132
- /**
133
- * Compare a conventional-commits prefix on two titles. Returns null if both
134
- * prefixes match or either is missing; otherwise returns { issue, mr } so the
135
- * caller can warn (e.g. issue is "feat:" but mrTitle starts "fix:").
136
- */
137
- export function prefixDivergence(issueTitle, mrTitle) {
138
- if (!issueTitle || !mrTitle) return null;
139
- const re = /^([a-zA-Z]+)(?:\([^)]*\))?!?:/;
140
- const ip = issueTitle.match(re)?.[1]?.toLowerCase();
141
- const mp = mrTitle.match(re)?.[1]?.toLowerCase();
142
- if (!ip || !mp) return null;
143
- if (ip === mp) return null;
144
- return { issue: ip, mr: mp };
145
- }
@@ -1,280 +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 { getFlag, projectRoot } from "./utils.js";
6
- import {
7
- PHASES,
8
- appendEvent,
9
- readAnalysis,
10
- readState,
11
- resolveSessionTarget,
12
- updateState,
13
- } from "./sessionStore.js";
14
-
15
- function StepRow({ step, status }) {
16
- let glyph;
17
- let color;
18
- if (status === "running") {
19
- glyph = h`<${Spinner} type="dots"/>`;
20
- color = "cyan";
21
- } else if (status === "passed") {
22
- glyph = h`<${Text}>✓</>`;
23
- color = "green";
24
- } else if (status === "failed") {
25
- glyph = h`<${Text}>✘</>`;
26
- color = "red";
27
- } else {
28
- glyph = h`<${Text}>○</>`;
29
- color = "gray";
30
- }
31
- return h`<${Box} marginLeft=${4}>
32
- <${Text} color=${color}>${glyph}</>
33
- <${Text}> ${step}</>
34
- </>`;
35
- }
36
-
37
- function RepoBlock({ repo, projectType, steps, status, allPassed }) {
38
- const headerColor = status === "done" ? (allPassed ? "green" : "red") : "yellow";
39
- const headerText =
40
- status === "done"
41
- ? allPassed
42
- ? `✓ ${repo} (${projectType ?? "?"}) — all checks passed`
43
- : `✘ ${repo} (${projectType ?? "?"}) — failed`
44
- : `▸ ${repo} (${projectType ?? "?"})`;
45
- return h`<${Box} flexDirection="column" marginTop=${1}>
46
- <${Text} color=${headerColor}>${headerText}</>
47
- ${steps.map((s, i) => h`<${StepRow} key=${i} ...${s} />`)}
48
- </>`;
49
- }
50
-
51
- function LogLine({ message, repo }) {
52
- return h`<${Box} marginLeft=${4}>
53
- <${Text} dimColor>${repo ? `[${repo}] ` : ""}${message}</>
54
- </>`;
55
- }
56
-
57
- function ValidateView({ analysis, eventSink, onDone }) {
58
- const { exit } = useApp();
59
- const [repos, setRepos] = useState({});
60
- const [logs, setLogs] = useState([]);
61
- const [finished, setFinished] = useState(null);
62
-
63
- useEffect(() => {
64
- if (!finished) return;
65
- onDone(finished);
66
- exit();
67
- }, [finished]);
68
-
69
- useEffect(() => {
70
- let cancelled = false;
71
- (async () => {
72
- try {
73
- const { validate } = await import(
74
- path.join(projectRoot, "server/forge/core/validator.js")
75
- );
76
- let allOk = true;
77
-
78
- const onEvent = (evt) => {
79
- if (cancelled) return;
80
- eventSink?.(evt);
81
- const repo = evt.repo;
82
-
83
- if (evt.type === "repo_started" && repo) {
84
- setRepos((prev) => ({
85
- ...prev,
86
- [repo]: {
87
- projectType: evt.projectType,
88
- stepOrder: evt.stepOrder ?? [],
89
- steps: (evt.stepOrder ?? []).map((s) => ({
90
- step: s,
91
- status: "pending",
92
- })),
93
- status: "running",
94
- allPassed: null,
95
- },
96
- }));
97
- return;
98
- }
99
-
100
- if (evt.type === "progress" && repo && evt.step) {
101
- setRepos((prev) => {
102
- const cur = prev[repo];
103
- if (!cur) return prev;
104
- return {
105
- ...prev,
106
- [repo]: {
107
- ...cur,
108
- steps: cur.steps.map((s) =>
109
- s.step === evt.step ? { ...s, status: "running" } : s,
110
- ),
111
- },
112
- };
113
- });
114
- return;
115
- }
116
-
117
- if (evt.type === "step_result" && repo && evt.step) {
118
- setRepos((prev) => {
119
- const cur = prev[repo];
120
- if (!cur) return prev;
121
- return {
122
- ...prev,
123
- [repo]: {
124
- ...cur,
125
- steps: cur.steps.map((s) =>
126
- s.step === evt.step
127
- ? { ...s, status: evt.passed ? "passed" : "failed" }
128
- : s,
129
- ),
130
- },
131
- };
132
- });
133
- if (!evt.passed) allOk = false;
134
- return;
135
- }
136
-
137
- if (evt.type === "repo_result" && repo) {
138
- setRepos((prev) => {
139
- const cur = prev[repo];
140
- if (!cur) return prev;
141
- return {
142
- ...prev,
143
- [repo]: { ...cur, status: "done", allPassed: evt.allPassed },
144
- };
145
- });
146
- if (!evt.allPassed) allOk = false;
147
- return;
148
- }
149
-
150
- if (evt.type === "log") {
151
- setLogs((prev) => [
152
- ...prev,
153
- { message: evt.message, repo: evt.repo },
154
- ]);
155
- }
156
- };
157
-
158
- const result = await validate(analysis, onEvent);
159
- if (cancelled) return;
160
- setFinished({ ok: true, allPassed: allOk && result.allPassed, result });
161
- } catch (err) {
162
- if (cancelled) return;
163
- setLogs((prev) => [
164
- ...prev,
165
- { message: `ERROR: ${err.message ?? String(err)}` },
166
- ]);
167
- setFinished({ ok: false, error: err });
168
- }
169
- })();
170
- return () => {
171
- cancelled = true;
172
- };
173
- }, []);
174
-
175
- return h`<${Box} flexDirection="column">
176
- <${Text}>${`Validating ${analysis.changes?.length ?? 0} change(s)…`}</>
177
- ${Object.entries(repos).map(([repo, r], i) =>
178
- h`<${RepoBlock} key=${i} repo=${repo} ...${r} />`,
179
- )}
180
- ${logs.length > 0
181
- ? h`<${Box} flexDirection="column" marginTop=${1}>
182
- <${Text} dimColor>${"─── log ───"}</>
183
- ${logs.slice(-12).map((l, i) => h`<${LogLine} key=${i} ...${l} />`)}
184
- </>`
185
- : null}
186
- ${finished
187
- ? h`<${Box} marginTop=${1}>
188
- <${Text} color=${finished.ok && finished.allPassed ? "green" : "red"}>
189
- ${finished.ok && finished.allPassed ? "✓ All checks passed." : "✘ Validation failed."}
190
- </>
191
- </>`
192
- : null}
193
- </>`;
194
- }
195
-
196
- export async function cmdValidate(argv) {
197
- const json = argv.includes("--json");
198
- const explicitIid = Number(argv[1]);
199
- const explicitRepo = getFlag(argv, "--repo");
200
- const target = await resolveSessionTarget({
201
- explicitIid: Number.isInteger(explicitIid) && explicitIid > 0 ? explicitIid : null,
202
- explicitRepo,
203
- });
204
- if (!target?.repo || !target?.iid) {
205
- console.error(
206
- "No session target. Pass an <iid>, set --repo, or run analyze first.",
207
- );
208
- process.exit(1);
209
- }
210
- const analysis = await readAnalysis(target.repo, target.iid);
211
- if (!analysis) {
212
- console.error(
213
- `No cached analysis at ${target.repo}#${target.iid}. Run: forge analyze ${target.iid}`,
214
- );
215
- process.exit(1);
216
- }
217
-
218
- if (json) {
219
- const { validate } = await import(
220
- path.join(projectRoot, "server/forge/core/validator.js")
221
- );
222
- const events = [];
223
- const onEvent = (evt) => {
224
- events.push(evt);
225
- process.stdout.write(`${JSON.stringify(evt)}\n`);
226
- };
227
- try {
228
- const result = await validate(analysis, onEvent);
229
- await persistValidateOutcome(target.repo, target.iid, events, result.allPassed, null);
230
- if (!result.allPassed) process.exit(2);
231
- } catch (err) {
232
- process.stdout.write(
233
- `${JSON.stringify({ type: "error", message: err.message })}\n`,
234
- );
235
- await persistValidateOutcome(target.repo, target.iid, events, false, err);
236
- process.exit(2);
237
- }
238
- return;
239
- }
240
-
241
- let outcome = { ok: false };
242
- const events = [];
243
- await runApp(
244
- h`<${ValidateView}
245
- analysis=${analysis}
246
- eventSink=${(e) => events.push(e)}
247
- onDone=${(o) => (outcome = o)} />`,
248
- );
249
- await persistValidateOutcome(
250
- target.repo,
251
- target.iid,
252
- events,
253
- outcome.ok && outcome.allPassed,
254
- outcome.error,
255
- );
256
- if (!outcome.ok || !outcome.allPassed) process.exit(2);
257
- }
258
-
259
- async function persistValidateOutcome(repo, iid, events, passed, error) {
260
- try {
261
- await updateState(repo, iid, {
262
- validation: {
263
- passed: !!passed,
264
- checkedAt: new Date().toISOString(),
265
- error: error?.message ?? null,
266
- },
267
- phase: passed ? PHASES.VALIDATED : "validate-failed",
268
- lastError: error?.message ?? null,
269
- });
270
- await appendEvent(repo, iid, { type: "session:validate-start" });
271
- for (const e of events) await appendEvent(repo, iid, e);
272
- await appendEvent(repo, iid, {
273
- type: "session:validate-end",
274
- passed: !!passed,
275
- error: error?.message,
276
- });
277
- } catch (err) {
278
- process.stderr.write(`forge: failed to persist session: ${err.message}\n`);
279
- }
280
- }