@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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../../../../server/forge/core/validator/fs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,wBAAsB,MAAM,CAAC,CAAC,EAAE,MAAM,oBAErC"}
@@ -1,9 +1,14 @@
1
1
  /**
2
2
  * Tiny fs helpers shared by clone / overlay / project-type detection.
3
3
  */
4
-
5
4
  import fs from "node:fs/promises";
6
-
7
5
  export async function exists(p) {
8
- try { await fs.access(p); return true; } catch { return false; }
6
+ try {
7
+ await fs.access(p);
8
+ return true;
9
+ }
10
+ catch {
11
+ return false;
12
+ }
9
13
  }
14
+ //# sourceMappingURL=fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fs.js","sourceRoot":"","sources":["../../../../../server/forge/core/validator/fs.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,CAAS;IACrC,IAAI,CAAC;QAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,KAAK,CAAC;IAAC,CAAC;AACjE,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Overlay proposed changes onto the cloned working tree, and read formatter
3
+ * rewrites back into the analysis so committed text matches what was validated.
4
+ */
5
+ import type { ForgeChange, ForgeEvent } from "../types.ts";
6
+ export declare function overlayChanges(dir: string, changes: ForgeChange[], emit: (event: ForgeEvent) => void): Promise<void>;
7
+ /**
8
+ * After a formatter step (npm run format, go fmt …), re-read the overlayed
9
+ * files. If the formatter rewrote any of them, mutate `c.proposed` in place
10
+ * and emit a `changes_formatted` event so the frontend can sync.
11
+ */
12
+ export declare function readbackFormatted(dir: string, changes: ForgeChange[], emit: (event: ForgeEvent) => void): Promise<void>;
13
+ //# sourceMappingURL=overlay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay.d.ts","sourceRoot":"","sources":["../../../../../server/forge/core/validator/overlay.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE3D,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,iBAkB1G;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,iBAkB7G"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Overlay proposed changes onto the cloned working tree, and read formatter
3
+ * rewrites back into the analysis so committed text matches what was validated.
4
+ */
5
+ import fs from "node:fs/promises";
6
+ import path from "node:path";
7
+ export async function overlayChanges(dir, changes, emit) {
8
+ emit({ type: "progress", step: "overlay", status: "running" });
9
+ const realBase = await fs.realpath(dir);
10
+ for (const c of changes) {
11
+ const full = path.resolve(dir, c.file);
12
+ if (!full.startsWith(realBase + path.sep) && full !== realBase) {
13
+ throw new Error(`Refusing to write outside clone dir: ${c.file}`);
14
+ }
15
+ if (c.action === "delete") {
16
+ await fs.rm(full, { force: true });
17
+ emit({ type: "log", step: "overlay", message: `- ${c.file}` });
18
+ }
19
+ else {
20
+ await fs.mkdir(path.dirname(full), { recursive: true });
21
+ await fs.writeFile(full, c.proposed);
22
+ emit({ type: "log", step: "overlay", message: `+ ${c.file} (${c.action})` });
23
+ }
24
+ }
25
+ emit({ type: "step_result", step: "overlay", passed: true });
26
+ }
27
+ /**
28
+ * After a formatter step (npm run format, go fmt …), re-read the overlayed
29
+ * files. If the formatter rewrote any of them, mutate `c.proposed` in place
30
+ * and emit a `changes_formatted` event so the frontend can sync.
31
+ */
32
+ export async function readbackFormatted(dir, changes, emit) {
33
+ const updated = [];
34
+ for (const c of changes) {
35
+ if (c.action === "delete")
36
+ continue;
37
+ try {
38
+ const after = await fs.readFile(path.join(dir, c.file), "utf8");
39
+ if (after !== c.proposed) {
40
+ c.proposed = after;
41
+ updated.push({ file: c.file, proposed: after });
42
+ }
43
+ }
44
+ catch {
45
+ // File may have been removed or renamed by the formatter
46
+ // (uncommon for prettier/eslint --fix); skip silently.
47
+ }
48
+ }
49
+ if (updated.length) {
50
+ emit({ type: "changes_formatted", files: updated });
51
+ }
52
+ }
53
+ //# sourceMappingURL=overlay.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlay.js","sourceRoot":"","sources":["../../../../../server/forge/core/validator/overlay.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAI7B,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW,EAAE,OAAsB,EAAE,IAAiC;IAC1G,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC/D,MAAM,QAAQ,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;YAChE,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;QACD,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC3B,MAAM,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACnC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACP,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACxD,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;YACrC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;QAC9E,CAAC;IACF,CAAC;IACD,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,GAAW,EAAE,OAAsB,EAAE,IAAiC;IAC7G,MAAM,OAAO,GAAG,EAAE,CAAC;IACnB,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ;YAAE,SAAS;QACpC,IAAI,CAAC;YACJ,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;YAChE,IAAI,KAAK,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;gBAC1B,CAAC,CAAC,QAAQ,GAAG,KAAK,CAAC;gBACnB,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;YACjD,CAAC;QACF,CAAC;QAAC,MAAM,CAAC;YACR,yDAAyD;YACzD,uDAAuD;QACxD,CAAC;IACF,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,IAAI,CAAC,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;IACrD,CAAC;AACF,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Per-repo project-type resolution.
3
+ *
4
+ * Resolution order (strongest → weakest signal):
5
+ * 1. configStore — the user's explicit choice in Settings → Repositories.
6
+ * Honoured verbatim when not "auto".
7
+ * 2. Changeset extensions — what the user is actually editing. Safe to
8
+ * trust as the primary signal because auto-attribution upstream
9
+ * guarantees that only repos that actually own a change ever reach
10
+ * this resolver. A polyglot repo with a root `package.json` and a
11
+ * nested Go module still classifies as Go when the changeset is `.go`.
12
+ * 3. Root marker — `go.mod` / `package.json` at the repo root.
13
+ * 4. Shallow tree scan — last resort, walks one level to find a nested
14
+ * marker (monorepo layouts).
15
+ * 5. Fallback — Node.
16
+ */
17
+ import type { ForgeChange } from "../types.ts";
18
+ export declare const SUPPORTED_TYPES: string[];
19
+ export declare function resolveProjectType(repoId: string, dir: string, changes: ForgeChange[]): Promise<string>;
20
+ //# sourceMappingURL=projectType.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectType.d.ts","sourceRoot":"","sources":["../../../../../server/forge/core/validator/projectType.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG/C,eAAO,MAAM,eAAe,UAAiB,CAAC;AAuD9C,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,mBAc3F"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Per-repo project-type resolution.
3
+ *
4
+ * Resolution order (strongest → weakest signal):
5
+ * 1. configStore — the user's explicit choice in Settings → Repositories.
6
+ * Honoured verbatim when not "auto".
7
+ * 2. Changeset extensions — what the user is actually editing. Safe to
8
+ * trust as the primary signal because auto-attribution upstream
9
+ * guarantees that only repos that actually own a change ever reach
10
+ * this resolver. A polyglot repo with a root `package.json` and a
11
+ * nested Go module still classifies as Go when the changeset is `.go`.
12
+ * 3. Root marker — `go.mod` / `package.json` at the repo root.
13
+ * 4. Shallow tree scan — last resort, walks one level to find a nested
14
+ * marker (monorepo layouts).
15
+ * 5. Fallback — Node.
16
+ */
17
+ import fs from "node:fs/promises";
18
+ import path from "node:path";
19
+ import * as configStore from "../configStore.js";
20
+ import { exists } from "./fs.js";
21
+ export const SUPPORTED_TYPES = ["node", "go"];
22
+ const GO_EXTS = new Set([".go"]);
23
+ const NODE_EXTS = new Set([".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"]);
24
+ function detectFromChanges(changes) {
25
+ let go = 0;
26
+ let node = 0;
27
+ for (const c of changes ?? []) {
28
+ const ext = path.extname(c.file ?? "").toLowerCase();
29
+ if (GO_EXTS.has(ext))
30
+ go++;
31
+ else if (NODE_EXTS.has(ext))
32
+ node++;
33
+ }
34
+ if (go > 0 && node === 0)
35
+ return "go";
36
+ if (node > 0 && go === 0)
37
+ return "node";
38
+ return null;
39
+ }
40
+ async function detectFromRoot(dir) {
41
+ const hasGo = await exists(path.join(dir, "go.mod"));
42
+ const hasNode = await exists(path.join(dir, "package.json"));
43
+ if (hasGo && !hasNode)
44
+ return "go";
45
+ if (hasNode && !hasGo)
46
+ return "node";
47
+ if (hasGo && hasNode)
48
+ return "go"; // both at root — prefer the more specific toolchain.
49
+ return null;
50
+ }
51
+ async function detectFromShallowScan(dir) {
52
+ let hasGo = false;
53
+ let hasNode = false;
54
+ try {
55
+ const entries = await fs.readdir(dir, { withFileTypes: true });
56
+ for (const entry of entries) {
57
+ if (!entry.isDirectory())
58
+ continue;
59
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "vendor")
60
+ continue;
61
+ const sub = path.join(dir, entry.name);
62
+ if (!hasGo && (await exists(path.join(sub, "go.mod"))))
63
+ hasGo = true;
64
+ if (!hasNode && (await exists(path.join(sub, "package.json"))))
65
+ hasNode = true;
66
+ if (hasGo && hasNode)
67
+ break;
68
+ }
69
+ }
70
+ catch {
71
+ // readdir failed — fall through with whatever we already have.
72
+ }
73
+ if (hasGo && !hasNode)
74
+ return "go";
75
+ if (hasNode && !hasGo)
76
+ return "node";
77
+ if (hasGo)
78
+ return "go";
79
+ return null;
80
+ }
81
+ export async function resolveProjectType(repoId, dir, changes) {
82
+ const repos = (configStore.get().repos ?? []);
83
+ const cfg = repos.find((r) => r.id === repoId);
84
+ const configured = cfg?.projectType;
85
+ if (configured && configured !== "auto")
86
+ return configured;
87
+ // Auto-attribution already filtered out misattributed changes upstream,
88
+ // so the changeset here genuinely reflects this repo's diff. Trust it.
89
+ return (detectFromChanges(changes)
90
+ ?? (await detectFromRoot(dir))
91
+ ?? (await detectFromShallowScan(dir))
92
+ ?? "node");
93
+ }
94
+ //# sourceMappingURL=projectType.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectType.js","sourceRoot":"","sources":["../../../../../server/forge/core/validator/projectType.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Go runner: emits Go-canonical step names so the UI shows what's actually
3
+ * being run instead of forcing Node-isms onto the toolchain.
4
+ *
5
+ * fmt → go fmt ./... (rewrites .go files; readback feeds the diff)
6
+ * vet → go vet ./... (replaced by golangci-lint when installed)
7
+ * build → go build ./... (compile + type check)
8
+ * test → go test -race -count=1 ./...
9
+ *
10
+ * Module location: `go` commands need to run inside the directory containing
11
+ * the relevant go.mod. Monorepos commonly nest the module under e.g.
12
+ * `services/api/go.mod`, so we walk up from a changed .go file (and shallow-
13
+ * scan the clone) to find it. Multi-module repos with several go.mod files
14
+ * are out of scope for v1 — we use whichever module the changeset hits first.
15
+ */
16
+ import type { ForgeChange, ForgeEvent } from "../../types.ts";
17
+ import type { StepResult } from "../cmd.ts";
18
+ export declare const STEP_ORDER: string[];
19
+ export declare const FIXABLE_STEPS: string[];
20
+ export declare function runGoRepo(cloneDir: string, changes: ForgeChange[], emit: (event: ForgeEvent) => void): Promise<Record<string, StepResult>>;
21
+ //# sourceMappingURL=go.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"go.d.ts","sourceRoot":"","sources":["../../../../../../server/forge/core/validator/runners/go.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAK5C,eAAO,MAAM,UAAU,UAAiE,CAAC;AACzF,eAAO,MAAM,aAAa,UAA2B,CAAC;AAkDtD,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,uCA+B1G"}
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Go runner: emits Go-canonical step names so the UI shows what's actually
3
+ * being run instead of forcing Node-isms onto the toolchain.
4
+ *
5
+ * fmt → go fmt ./... (rewrites .go files; readback feeds the diff)
6
+ * vet → go vet ./... (replaced by golangci-lint when installed)
7
+ * build → go build ./... (compile + type check)
8
+ * test → go test -race -count=1 ./...
9
+ *
10
+ * Module location: `go` commands need to run inside the directory containing
11
+ * the relevant go.mod. Monorepos commonly nest the module under e.g.
12
+ * `services/api/go.mod`, so we walk up from a changed .go file (and shallow-
13
+ * scan the clone) to find it. Multi-module repos with several go.mod files
14
+ * are out of scope for v1 — we use whichever module the changeset hits first.
15
+ */
16
+ import fs from "node:fs/promises";
17
+ import path from "node:path";
18
+ import { runCmd, runStep } from "../cmd.js";
19
+ import { exists } from "../fs.js";
20
+ import { readbackFormatted } from "../overlay.js";
21
+ // Public step contract for the orchestrator → frontend handshake.
22
+ export const STEP_ORDER = ["clone", "install", "overlay", "fmt", "vet", "build", "test"];
23
+ export const FIXABLE_STEPS = ["vet", "build", "test"];
24
+ async function which(bin) {
25
+ const r = await runCmd("which", [bin]);
26
+ return r.code === 0;
27
+ }
28
+ async function findGoModDir(cloneDir, changes) {
29
+ // Walk up from the first changed .go file looking for the nearest go.mod.
30
+ const goChange = (changes ?? []).find((c) => path.extname(c.file ?? "").toLowerCase() === ".go");
31
+ if (goChange) {
32
+ let cur = path.dirname(path.join(cloneDir, goChange.file));
33
+ while (cur.startsWith(cloneDir)) {
34
+ if (await exists(path.join(cur, "go.mod")))
35
+ return cur;
36
+ const parent = path.dirname(cur);
37
+ if (parent === cur)
38
+ break;
39
+ cur = parent;
40
+ }
41
+ }
42
+ if (await exists(path.join(cloneDir, "go.mod")))
43
+ return cloneDir;
44
+ // Shallow scan for monorepos with no .go changes (e.g. an MR that only
45
+ // touches a config file but the module is in a subdir).
46
+ try {
47
+ const entries = await fs.readdir(cloneDir, { withFileTypes: true });
48
+ for (const entry of entries) {
49
+ if (!entry.isDirectory())
50
+ continue;
51
+ if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "vendor")
52
+ continue;
53
+ const sub = path.join(cloneDir, entry.name);
54
+ if (await exists(path.join(sub, "go.mod")))
55
+ return sub;
56
+ }
57
+ }
58
+ catch {
59
+ // fall through
60
+ }
61
+ return null;
62
+ }
63
+ async function ensureInstalled(goDir, emit) {
64
+ emit({ type: "progress", step: "install", status: "running" });
65
+ emit({ type: "log", step: "install", message: "Running go mod download…" });
66
+ const r = await runCmd("go", ["mod", "download"], { cwd: goDir }, (l) => emit({ type: "log", step: "install", message: l }));
67
+ if (r.code !== 0)
68
+ throw new Error(`go mod download failed (code ${r.code})`);
69
+ emit({ type: "step_result", step: "install", passed: true });
70
+ }
71
+ export async function runGoRepo(cloneDir, changes, emit) {
72
+ const goDir = await findGoModDir(cloneDir, changes);
73
+ if (!goDir) {
74
+ throw new Error("Couldn't locate a go.mod in the clone — repo doesn't look like a Go module.");
75
+ }
76
+ const rel = path.relative(cloneDir, goDir) || ".";
77
+ if (rel !== ".")
78
+ emit({ type: "log", step: "overlay", message: `Go module rooted at ${rel}` });
79
+ await ensureInstalled(goDir, emit);
80
+ const results = {};
81
+ results.fmt = await runStep(goDir, "fmt", "go", ["fmt", "./..."], emit);
82
+ if (results.fmt.passed)
83
+ await readbackFormatted(cloneDir, changes, emit);
84
+ // `go vet` is the baseline. If the repo (or developer) has golangci-lint
85
+ // installed, prefer it — it covers gofmt, vet, staticcheck, errcheck, and
86
+ // more in a single pass and is the de-facto community standard.
87
+ if (await which("golangci-lint")) {
88
+ results.vet = await runStep(goDir, "vet", "golangci-lint", ["run", "./..."], emit);
89
+ }
90
+ else {
91
+ results.vet = await runStep(goDir, "vet", "go", ["vet", "./..."], emit);
92
+ }
93
+ results.build = await runStep(goDir, "build", "go", ["build", "./..."], emit);
94
+ // -race catches data races; -count=1 disables the test cache so we always
95
+ // re-execute against the overlayed source. Both are standard Go CI practice.
96
+ results.test = await runStep(goDir, "test", "go", ["test", "-race", "-count=1", "./..."], emit);
97
+ return results;
98
+ }
99
+ //# sourceMappingURL=go.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"go.js","sourceRoot":"","sources":["../../../../../../server/forge/core/validator/runners/go.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,kEAAkE;AAClE,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AACzF,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;AAEtD,KAAK,UAAU,KAAK,CAAC,GAAW;IAC/B,MAAM,CAAC,GAAG,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,OAAO,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC;AACrB,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,QAAgB,EAAE,OAAsB;IACnE,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAc,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,KAAK,KAAK,CAAC,CAAC;IAC9G,IAAI,QAAQ,EAAE,CAAC;QACd,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3D,OAAO,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;YACjC,IAAI,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;gBAAE,OAAO,GAAG,CAAC;YACvD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACjC,IAAI,MAAM,KAAK,GAAG;gBAAE,MAAM;YAC1B,GAAG,GAAG,MAAM,CAAC;QACd,CAAC;IACF,CAAC;IAED,IAAI,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IAEjE,uEAAuE;IACvE,wDAAwD;IACxD,IAAI,CAAC;QACJ,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACpE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE;gBAAE,SAAS;YACnC,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;gBAAE,SAAS;YACrG,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5C,IAAI,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;gBAAE,OAAO,GAAG,CAAC;QACxD,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,eAAe;IAChB,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,KAAa,EAAE,IAAiC;IAC9E,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IAC/D,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,EAAE,CAAC,CAAC;IAC5E,MAAM,CAAC,GAAG,MAAM,MAAM,CACrB,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,CAAC,EACzB,EAAE,GAAG,EAAE,KAAK,EAAE,EACd,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,CACjE,CAAC;IACF,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;IAC7E,IAAI,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,QAAgB,EAAE,OAAsB,EAAE,IAAiC;IAC1G,MAAM,KAAK,GAAG,MAAM,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACpD,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,6EAA6E,CAAC,CAAC;IAChG,CAAC;IACD,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,IAAI,GAAG,CAAC;IAClD,IAAI,GAAG,KAAK,GAAG;QAAE,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,uBAAuB,GAAG,EAAE,EAAE,CAAC,CAAC;IAE/F,MAAM,eAAe,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAEnC,MAAM,OAAO,GAA+B,EAAE,CAAC;IAE/C,OAAO,CAAC,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IACxE,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM;QAAE,MAAM,iBAAiB,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IAEzE,yEAAyE;IACzE,0EAA0E;IAC1E,gEAAgE;IAChE,IAAI,MAAM,KAAK,CAAC,eAAe,CAAC,EAAE,CAAC;QAClC,OAAO,CAAC,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,eAAe,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IACpF,CAAC;SAAM,CAAC;QACP,OAAO,CAAC,GAAG,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,CAAC,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IAE9E,0EAA0E;IAC1E,6EAA6E;IAC7E,OAAO,CAAC,IAAI,GAAG,MAAM,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;IAEhG,OAAO,OAAO,CAAC;AAChB,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Node runner: `npm ci` (lockfile-hash cached) followed by the standard
3
+ * format / lint / typecheck / test scripts. Missing scripts are skipped, not
4
+ * failed. The formatter step is read back so its rewrites flow into the
5
+ * proposal the user commits.
6
+ */
7
+ import type { ForgeChange, ForgeEvent } from "../../types.ts";
8
+ import type { StepResult } from "../cmd.ts";
9
+ export declare const STEP_ORDER: string[];
10
+ export declare const FIXABLE_STEPS: string[];
11
+ export declare function runNodeRepo(dir: string, changes: ForgeChange[], emit: (event: ForgeEvent) => void): Promise<Record<string, StepResult>>;
12
+ //# sourceMappingURL=node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../../../../../server/forge/core/validator/runners/node.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAU5C,eAAO,MAAM,UAAU,UAAyF,CAAC;AACjH,eAAO,MAAM,aAAa,UAAgC,CAAC;AAuC3D,wBAAsB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,uCAsBvG"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Node runner: `npm ci` (lockfile-hash cached) followed by the standard
3
+ * format / lint / typecheck / test scripts. Missing scripts are skipped, not
4
+ * failed. The formatter step is read back so its rewrites flow into the
5
+ * proposal the user commits.
6
+ */
7
+ import crypto from "node:crypto";
8
+ import fs from "node:fs/promises";
9
+ import path from "node:path";
10
+ import { runCmd, runStep } from "../cmd.js";
11
+ import { exists } from "../fs.js";
12
+ import { readbackFormatted } from "../overlay.js";
13
+ import { runTddPrecheckNode } from "../tdd.js";
14
+ const SCRIPTS = ["format", "lint", "typecheck", "test"];
15
+ // Public step contract for the orchestrator → frontend handshake. `tdd_precheck`
16
+ // sits before `test`; it only actually runs (and appears in the UI) when the
17
+ // proposal bundles new test files with impl changes — see runTddPrecheckNode.
18
+ export const STEP_ORDER = ["clone", "install", "overlay", "format", "lint", "typecheck", "tdd_precheck", "test"];
19
+ export const FIXABLE_STEPS = ["lint", "typecheck", "test"];
20
+ async function readPackageScripts(dir) {
21
+ try {
22
+ const raw = await fs.readFile(path.join(dir, "package.json"), "utf8");
23
+ return JSON.parse(raw).scripts ?? {};
24
+ }
25
+ catch {
26
+ return {};
27
+ }
28
+ }
29
+ async function ensureInstalled(dir, emit) {
30
+ const lockPath = path.join(dir, "package-lock.json");
31
+ if (!(await exists(lockPath)))
32
+ return; // not an npm-managed project — omit the row entirely.
33
+ const hashPath = path.join(dir, "node_modules", ".forge-lock-hash");
34
+ const lockContent = await fs.readFile(lockPath);
35
+ const lockHash = crypto.createHash("sha256").update(lockContent).digest("hex");
36
+ if (await exists(hashPath)) {
37
+ const stored = (await fs.readFile(hashPath, "utf8")).trim();
38
+ if (stored === lockHash)
39
+ return; // node_modules already matches; nothing to install.
40
+ }
41
+ emit({ type: "progress", step: "install", status: "running" });
42
+ emit({ type: "log", step: "install", message: "Running npm ci (first run for this lockfile may take a minute)…" });
43
+ const install = await runCmd("npm", ["ci", "--prefer-offline", "--no-audit", "--no-fund"], { cwd: dir }, (l) => emit({ type: "log", step: "install", message: l }));
44
+ if (install.code !== 0) {
45
+ throw new Error(`npm ci failed (code ${install.code})`);
46
+ }
47
+ await fs.mkdir(path.dirname(hashPath), { recursive: true });
48
+ await fs.writeFile(hashPath, lockHash);
49
+ emit({ type: "step_result", step: "install", passed: true });
50
+ }
51
+ export async function runNodeRepo(dir, changes, emit) {
52
+ await ensureInstalled(dir, emit);
53
+ const scripts = await readPackageScripts(dir);
54
+ const results = {};
55
+ // Only run (and thus only render) scripts the project actually defines —
56
+ // missing scripts are omitted from the UI rather than shown as "skipped".
57
+ for (const s of SCRIPTS) {
58
+ if (!scripts[s])
59
+ continue;
60
+ // TDD precheck runs only when the `test` script exists, a new test file
61
+ // is part of the proposal, and there's impl code to revert. It records
62
+ // its own step_result under `tdd_precheck`.
63
+ if (s === "test") {
64
+ const precheck = await runTddPrecheckNode(dir, changes, emit);
65
+ if (precheck)
66
+ results.tdd_precheck = precheck;
67
+ }
68
+ results[s] = await runStep(dir, s, "npm", ["run", "--silent", s], emit);
69
+ if (s === "format" && results[s].passed) {
70
+ await readbackFormatted(dir, changes, emit);
71
+ }
72
+ }
73
+ return results;
74
+ }
75
+ //# sourceMappingURL=node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"node.js","sourceRoot":"","sources":["../../../../../../server/forge/core/validator/runners/node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * TDD precheck: when a proposal adds *new* test files alongside impl changes,
3
+ * run just those tests with the impl reverted. If they pass, the tests don't
4
+ * actually exercise the new behavior — that's the gap we surface.
5
+ *
6
+ * Only fires when all three hold:
7
+ * - at least one new test file (action === "create" on a test path)
8
+ * - at least one non-test change (impl)
9
+ * - the project has a `test` script
10
+ *
11
+ * The precheck reverts impl files via git (or `rm` for untracked creates),
12
+ * runs vitest scoped to the new test paths, then re-overlays impl so the
13
+ * downstream `test` step sees the full proposal.
14
+ */
15
+ import type { ForgeChange, ForgeEvent } from "../types.ts";
16
+ export declare function isNodeTestFile(filePath: string): boolean;
17
+ export declare function partitionChangesNode(changes: ForgeChange[]): {
18
+ impl: ForgeChange[];
19
+ newTests: ForgeChange[];
20
+ };
21
+ /**
22
+ * @returns {Promise<null|{passed: boolean, exitCode: number}>} null when skipped.
23
+ */
24
+ export declare function runTddPrecheckNode(dir: string, changes: ForgeChange[], emit: (event: ForgeEvent) => void): Promise<{
25
+ passed: boolean;
26
+ exitCode: number;
27
+ durationMs: number;
28
+ tail: string;
29
+ }>;
30
+ //# sourceMappingURL=tdd.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tdd.d.ts","sourceRoot":"","sources":["../../../../../server/forge/core/validator/tdd.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAK3D,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,WAE9C;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,WAAW,EAAE;;;EAW1D;AA0BD;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,EAAE,IAAI,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI;;;;;GAsC9G"}
@@ -0,0 +1,96 @@
1
+ /**
2
+ * TDD precheck: when a proposal adds *new* test files alongside impl changes,
3
+ * run just those tests with the impl reverted. If they pass, the tests don't
4
+ * actually exercise the new behavior — that's the gap we surface.
5
+ *
6
+ * Only fires when all three hold:
7
+ * - at least one new test file (action === "create" on a test path)
8
+ * - at least one non-test change (impl)
9
+ * - the project has a `test` script
10
+ *
11
+ * The precheck reverts impl files via git (or `rm` for untracked creates),
12
+ * runs vitest scoped to the new test paths, then re-overlays impl so the
13
+ * downstream `test` step sees the full proposal.
14
+ */
15
+ import fs from "node:fs/promises";
16
+ import path from "node:path";
17
+ import { runCmd } from "./cmd.js";
18
+ const TEST_FILE_RE_NODE = /\.(test|spec)\.[cm]?[jt]sx?$/;
19
+ export function isNodeTestFile(filePath) {
20
+ return TEST_FILE_RE_NODE.test(filePath);
21
+ }
22
+ export function partitionChangesNode(changes) {
23
+ const impl = [];
24
+ const newTests = [];
25
+ for (const c of changes ?? []) {
26
+ if (isNodeTestFile(c.file)) {
27
+ if (c.action === "create")
28
+ newTests.push(c);
29
+ }
30
+ else {
31
+ impl.push(c);
32
+ }
33
+ }
34
+ return { impl, newTests };
35
+ }
36
+ async function revertImplOverlay(dir, impl) {
37
+ for (const c of impl) {
38
+ const full = path.join(dir, c.file);
39
+ if (c.action === "create") {
40
+ await fs.rm(full, { force: true });
41
+ }
42
+ else {
43
+ // modify or delete — restore the pre-overlay content from HEAD.
44
+ await runCmd("git", ["checkout", "--", c.file], { cwd: dir }, undefined);
45
+ }
46
+ }
47
+ }
48
+ async function reapplyImplOverlay(dir, impl) {
49
+ for (const c of impl) {
50
+ const full = path.join(dir, c.file);
51
+ if (c.action === "delete") {
52
+ await fs.rm(full, { force: true });
53
+ }
54
+ else {
55
+ await fs.mkdir(path.dirname(full), { recursive: true });
56
+ await fs.writeFile(full, c.proposed);
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * @returns {Promise<null|{passed: boolean, exitCode: number}>} null when skipped.
62
+ */
63
+ export async function runTddPrecheckNode(dir, changes, emit) {
64
+ const { impl, newTests } = partitionChangesNode(changes);
65
+ if (newTests.length === 0 || impl.length === 0)
66
+ return null;
67
+ const testPaths = newTests.map((c) => c.file);
68
+ emit({ type: "progress", step: "tdd_precheck", status: "running" });
69
+ emit({
70
+ type: "log",
71
+ step: "tdd_precheck",
72
+ message: `Verifying ${newTests.length} new test file(s) fail without impl: ${testPaths.join(", ")}`,
73
+ });
74
+ await revertImplOverlay(dir, impl);
75
+ const start = Date.now();
76
+ const res = await runCmd("npx", ["vitest", "--run", "--reporter=default", ...testPaths], { cwd: dir }, (l) => emit({ type: "log", step: "tdd_precheck", message: l }));
77
+ await reapplyImplOverlay(dir, impl);
78
+ // TDD contract: without the impl, the new tests must fail (non-zero exit).
79
+ // Exit 0 means either the tests passed without the impl — they don't
80
+ // exercise the new behavior — or vitest found nothing to run.
81
+ const precheckPassed = res.code !== 0;
82
+ const tail = res.output.split("\n").slice(-60).join("\n");
83
+ emit({
84
+ type: "step_result",
85
+ step: "tdd_precheck",
86
+ passed: precheckPassed,
87
+ exitCode: res.code,
88
+ durationMs: Date.now() - start,
89
+ tail,
90
+ message: precheckPassed
91
+ ? `${newTests.length} new test(s) failed without implementation (good TDD).`
92
+ : `${newTests.length} new test(s) passed without the implementation change — they may not actually test the fix.`,
93
+ });
94
+ return { passed: precheckPassed, exitCode: res.code, durationMs: Date.now() - start, tail };
95
+ }
96
+ //# sourceMappingURL=tdd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tdd.js","sourceRoot":"","sources":["../../../../../server/forge/core/validator/tdd.ts"],"names":[],"mappings":"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"}