forgeos 0.1.0-alpha.2 → 0.1.0-alpha.21

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 (406) hide show
  1. package/.npmignore +4 -0
  2. package/AGENTS.md +168 -81
  3. package/CHANGELOG.md +211 -0
  4. package/README.md +88 -14
  5. package/adapters/go/README.md +23 -0
  6. package/adapters/go/go.mod +3 -0
  7. package/adapters/go/http.go +149 -0
  8. package/adapters/go/registry.go +234 -0
  9. package/adapters/go/types.go +136 -0
  10. package/adapters/java/README.md +68 -0
  11. package/adapters/java/pom.xml +34 -0
  12. package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
  13. package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
  14. package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
  15. package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
  16. package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
  17. package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
  18. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
  19. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
  20. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
  21. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
  22. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
  23. package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
  24. package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
  25. package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
  26. package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
  27. package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
  28. package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
  29. package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
  30. package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
  31. package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
  32. package/adapters/java/target/classes/dev/forgeos/adapter/Auth.class +0 -0
  33. package/adapters/java/target/classes/dev/forgeos/adapter/Diagnostic.class +0 -0
  34. package/adapters/java/target/classes/dev/forgeos/adapter/Entry.class +0 -0
  35. package/adapters/java/target/classes/dev/forgeos/adapter/EntryKind.class +0 -0
  36. package/adapters/java/target/classes/dev/forgeos/adapter/ErrorInfo.class +0 -0
  37. package/adapters/java/target/classes/dev/forgeos/adapter/Forge.class +0 -0
  38. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeCall.class +0 -0
  39. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeContext.class +0 -0
  40. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeHandler.class +0 -0
  41. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeHttpHandler.class +0 -0
  42. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$EntryOption.class +0 -0
  43. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$RegisteredEntry.class +0 -0
  44. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry$RegistryOption.class +0 -0
  45. package/adapters/java/target/classes/dev/forgeos/adapter/ForgeRegistry.class +0 -0
  46. package/adapters/java/target/classes/dev/forgeos/adapter/Json.class +0 -0
  47. package/adapters/java/target/classes/dev/forgeos/adapter/Manifest.class +0 -0
  48. package/adapters/java/target/classes/dev/forgeos/adapter/RequestEnvelope.class +0 -0
  49. package/adapters/java/target/classes/dev/forgeos/adapter/ResponseEnvelope.class +0 -0
  50. package/adapters/java/target/classes/dev/forgeos/adapter/Risk.class +0 -0
  51. package/adapters/java/target/classes/dev/forgeos/adapter/Schemas.class +0 -0
  52. package/adapters/java/target/classes/dev/forgeos/adapter/Service.class +0 -0
  53. package/adapters/java/target/classes/dev/forgeos/adapter/TransactionMode.class +0 -0
  54. package/adapters/java/target/classes/dev/forgeos/adapter/TypedForgeHandler.class +0 -0
  55. package/adapters/java/target/forge-java-adapter-0.1.0-alpha.11.jar +0 -0
  56. package/adapters/java/target/maven-archiver/pom.properties +3 -0
  57. package/adapters/java/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +23 -0
  58. package/adapters/java/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +20 -0
  59. package/adapters/java-spring-boot-starter/README.md +32 -0
  60. package/adapters/java-spring-boot-starter/pom.xml +36 -0
  61. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
  62. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
  63. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
  64. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
  65. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
  66. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
  67. package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
  68. package/adapters/java-spring-boot-starter/target/classes/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
  69. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeCommand.class +0 -0
  70. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeExternalService.class +0 -0
  71. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeQuery.class +0 -0
  72. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.class +0 -0
  73. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.class +0 -0
  74. package/adapters/java-spring-boot-starter/target/classes/dev/forgeos/adapter/spring/ForgeSpringRuntime.class +0 -0
  75. package/adapters/java-spring-boot-starter/target/forge-java-spring-boot-starter-0.1.0-alpha.11.jar +0 -0
  76. package/adapters/java-spring-boot-starter/target/maven-archiver/pom.properties +3 -0
  77. package/adapters/java-spring-boot-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +6 -0
  78. package/adapters/java-spring-boot-starter/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +6 -0
  79. package/bin/forge.mjs +18 -0
  80. package/docs/changelog.md +242 -0
  81. package/docs/forge-protocol.md +189 -0
  82. package/examples/go-billing/go.mod +7 -0
  83. package/examples/go-billing/main.go +120 -0
  84. package/examples/java-billing/pom.xml +52 -0
  85. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
  86. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
  87. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
  88. package/examples/java-billing/target/classes/dev/forgeos/examples/billing/CreateInvoiceInput.class +0 -0
  89. package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Invoice.class +0 -0
  90. package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main$EmptyInput.class +0 -0
  91. package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main$Options.class +0 -0
  92. package/examples/java-billing/target/classes/dev/forgeos/examples/billing/Main.class +0 -0
  93. package/examples/java-billing/target/java-billing-0.1.0-alpha.11-all.jar +0 -0
  94. package/examples/java-billing/target/java-billing-0.1.0-alpha.11.jar +0 -0
  95. package/examples/java-billing/target/maven-archiver/pom.properties +3 -0
  96. package/examples/java-billing/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst +5 -0
  97. package/examples/java-billing/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst +3 -0
  98. package/package.json +29 -7
  99. package/schemas/forge-manifest.schema.json +57 -0
  100. package/src/forge/_generated/releaseManifest.json +1 -2
  101. package/src/forge/_generated/releaseManifest.ts +3 -3
  102. package/src/forge/agent-adapters/index.ts +1511 -123
  103. package/src/forge/agent-adapters/types.ts +216 -1
  104. package/src/forge/agent-memory/bridge.ts +1245 -0
  105. package/src/forge/agent-memory/context-pack.ts +151 -0
  106. package/src/forge/agent-memory/hook-runner.ts +312 -0
  107. package/src/forge/agent-memory/mcp.ts +224 -0
  108. package/src/forge/agent-memory/normalize.ts +498 -0
  109. package/src/forge/agent-memory/redaction.ts +103 -0
  110. package/src/forge/agent-memory/sources/claude-code.ts +51 -0
  111. package/src/forge/agent-memory/sources/codex-hook-runner.mjs +273 -0
  112. package/src/forge/agent-memory/sources/codex.ts +119 -0
  113. package/src/forge/agent-memory/sources/cursor.ts +35 -0
  114. package/src/forge/agent-memory/types.ts +191 -0
  115. package/src/forge/bench.ts +248 -0
  116. package/src/forge/brownfield-import/index.ts +801 -0
  117. package/src/forge/brownfield-import/types.ts +127 -0
  118. package/src/forge/cair/action-journal.ts +61 -0
  119. package/src/forge/cair/action-parser.ts +314 -0
  120. package/src/forge/cair/action-validator.ts +40 -0
  121. package/src/forge/cair/actions.ts +1818 -0
  122. package/src/forge/cair/format.ts +77 -0
  123. package/src/forge/cair/index.ts +106 -0
  124. package/src/forge/cair/query.ts +478 -0
  125. package/src/forge/cair/snapshot.ts +315 -0
  126. package/src/forge/cair/types.ts +248 -0
  127. package/src/forge/cli/ai.ts +671 -3
  128. package/src/forge/cli/auth.ts +36 -1
  129. package/src/forge/cli/build.ts +20 -4
  130. package/src/forge/cli/changed.ts +300 -0
  131. package/src/forge/cli/codex-app-server.ts +877 -0
  132. package/src/forge/cli/commands.ts +1285 -7
  133. package/src/forge/cli/db.ts +121 -2
  134. package/src/forge/cli/deps.ts +79 -12
  135. package/src/forge/cli/dev.ts +502 -38
  136. package/src/forge/cli/docs.ts +265 -0
  137. package/src/forge/cli/handoff.ts +250 -0
  138. package/src/forge/cli/index.ts +1 -0
  139. package/src/forge/cli/main.ts +49 -3
  140. package/src/forge/cli/new.ts +3 -1
  141. package/src/forge/cli/next-actions.ts +23 -0
  142. package/src/forge/cli/output.ts +290 -1
  143. package/src/forge/cli/parse.ts +770 -36
  144. package/src/forge/cli/query.ts +32 -0
  145. package/src/forge/cli/release.ts +35 -11
  146. package/src/forge/cli/rls.ts +568 -17
  147. package/src/forge/cli/run.ts +41 -0
  148. package/src/forge/cli/secrets.ts +46 -1
  149. package/src/forge/cli/security.ts +381 -0
  150. package/src/forge/cli/self-host.ts +56 -14
  151. package/src/forge/cli/studio.ts +2163 -0
  152. package/src/forge/cli/verify.ts +1422 -32
  153. package/src/forge/compiler/agent-contract/build.ts +725 -41
  154. package/src/forge/compiler/agent-contract/types.ts +85 -0
  155. package/src/forge/compiler/ai-registry/build.ts +62 -1
  156. package/src/forge/compiler/ai-registry/constants.ts +1 -1
  157. package/src/forge/compiler/ai-registry/parse.ts +168 -5
  158. package/src/forge/compiler/api-surface/build.ts +47 -0
  159. package/src/forge/compiler/app-graph/build.ts +68 -8
  160. package/src/forge/compiler/app-graph/extract.ts +107 -0
  161. package/src/forge/compiler/app-graph/forge-apis.ts +1 -0
  162. package/src/forge/compiler/app-graph/module-graph.ts +73 -78
  163. package/src/forge/compiler/app-graph/parser.ts +24 -24
  164. package/src/forge/compiler/app-graph/profile.ts +26 -0
  165. package/src/forge/compiler/app-graph/versions.ts +1 -1
  166. package/src/forge/compiler/classifier/capabilities.ts +3 -2
  167. package/src/forge/compiler/classifier/classify.ts +32 -8
  168. package/src/forge/compiler/classifier/secrets.ts +3 -2
  169. package/src/forge/compiler/classifier/signals.ts +91 -1
  170. package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
  171. package/src/forge/compiler/client-sdk/render-client.ts +188 -13
  172. package/src/forge/compiler/data-graph/parse.ts +3 -3
  173. package/src/forge/compiler/data-graph/sql/ddl.ts +60 -2
  174. package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
  175. package/src/forge/compiler/data-graph/sql/types.ts +1 -0
  176. package/src/forge/compiler/dev-manifest/build.ts +3 -0
  177. package/src/forge/compiler/diagnostics/codes.ts +35 -0
  178. package/src/forge/compiler/diagnostics/create.ts +8 -3
  179. package/src/forge/compiler/diagnostics/index.ts +2 -0
  180. package/src/forge/compiler/emitter/barrel.ts +3 -0
  181. package/src/forge/compiler/emitter/render.ts +5 -0
  182. package/src/forge/compiler/external-manifest/registry.ts +205 -0
  183. package/src/forge/compiler/external-manifest/types.ts +91 -0
  184. package/src/forge/compiler/external-manifest/validate.ts +373 -0
  185. package/src/forge/compiler/frontend-graph/build.ts +85 -13
  186. package/src/forge/compiler/integration/add.ts +498 -22
  187. package/src/forge/compiler/integration/snapshot.ts +2 -0
  188. package/src/forge/compiler/make-registry/build.ts +19 -7
  189. package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
  190. package/src/forge/compiler/orchestrator/plan.ts +78 -7
  191. package/src/forge/compiler/orchestrator/profile.ts +65 -0
  192. package/src/forge/compiler/orchestrator/run.ts +97 -31
  193. package/src/forge/compiler/orchestrator/serialize.ts +101 -8
  194. package/src/forge/compiler/package-graph/compiler.ts +13 -3
  195. package/src/forge/compiler/package-manager/adapter.ts +4 -1
  196. package/src/forge/compiler/package-manager/commands.ts +4 -0
  197. package/src/forge/compiler/package-manager/executor.ts +30 -1
  198. package/src/forge/compiler/policy-registry/build.ts +44 -1
  199. package/src/forge/compiler/test-graph/build.ts +11 -3
  200. package/src/forge/compiler/types/ai-registry.ts +25 -1
  201. package/src/forge/compiler/types/app-graph.ts +9 -2
  202. package/src/forge/compiler/types/cli.ts +76 -1
  203. package/src/forge/compiler/types/dev-manifest.ts +3 -0
  204. package/src/forge/compiler/types/frontend-graph.ts +2 -2
  205. package/src/forge/delta/classifier.ts +52 -0
  206. package/src/forge/delta/explain.ts +126 -0
  207. package/src/forge/delta/git-observer.ts +43 -0
  208. package/src/forge/delta/ids.ts +44 -0
  209. package/src/forge/delta/index.ts +13 -0
  210. package/src/forge/delta/recorder.ts +402 -0
  211. package/src/forge/delta/redaction.ts +50 -0
  212. package/src/forge/delta/schema.ts +240 -0
  213. package/src/forge/delta/session.ts +142 -0
  214. package/src/forge/delta/status.ts +489 -0
  215. package/src/forge/delta/store.ts +2975 -0
  216. package/src/forge/delta/timeline.ts +104 -0
  217. package/src/forge/dev/server.ts +768 -15
  218. package/src/forge/dev/types.ts +15 -1
  219. package/src/forge/dev/watch.ts +17 -7
  220. package/src/forge/dev-console/cycle.ts +233 -21
  221. package/src/forge/dev-console/types.ts +46 -1
  222. package/src/forge/impact/index.ts +46 -8
  223. package/src/forge/impact/types.ts +6 -0
  224. package/src/forge/intent/index.ts +35 -16
  225. package/src/forge/make/index.ts +149 -6
  226. package/src/forge/make/templates.ts +343 -2
  227. package/src/forge/make/types.ts +3 -1
  228. package/src/forge/refactor/index.ts +1 -0
  229. package/src/forge/repair/rules/index.ts +2 -2
  230. package/src/forge/review/index.ts +158 -12
  231. package/src/forge/review/types.ts +15 -0
  232. package/src/forge/runtime/ai/context.ts +210 -5
  233. package/src/forge/runtime/ai/types.ts +70 -0
  234. package/src/forge/runtime/auth/claims.ts +32 -0
  235. package/src/forge/runtime/auth/errors.ts +2 -0
  236. package/src/forge/runtime/context/create-context.ts +30 -6
  237. package/src/forge/runtime/db/generated-client.ts +13 -2
  238. package/src/forge/runtime/db/memory-adapter.ts +2 -2
  239. package/src/forge/runtime/db/pglite-adapter.ts +77 -2
  240. package/src/forge/runtime/db/postgres-adapter.ts +6 -3
  241. package/src/forge/runtime/executor.ts +112 -2
  242. package/src/forge/runtime/external/bridge.ts +649 -0
  243. package/src/forge/runtime/runner/run-entry.ts +16 -7
  244. package/src/forge/runtime/telemetry/scrubber.ts +91 -10
  245. package/src/forge/runtime/webhooks/security.ts +184 -0
  246. package/src/forge/server.ts +100 -2
  247. package/src/forge/version.ts +1 -1
  248. package/src/forge/vue/index.ts +407 -0
  249. package/src/forge/workspace/change-summary.ts +209 -0
  250. package/src/forge/workspace/forge-cli.ts +14 -0
  251. package/src/forge/workspace/git-summary.ts +279 -0
  252. package/templates/agent-workroom/AGENTS.md +29 -0
  253. package/templates/agent-workroom/README.md +34 -0
  254. package/templates/agent-workroom/forge.config.ts +3 -0
  255. package/templates/agent-workroom/package.json +33 -0
  256. package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
  257. package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
  258. package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
  259. package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
  260. package/templates/agent-workroom/src/forge/schema.ts +54 -0
  261. package/templates/agent-workroom/src/policies.ts +6 -0
  262. package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
  263. package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
  264. package/templates/agent-workroom/tsconfig.json +16 -0
  265. package/templates/agent-workroom/web/index.html +12 -0
  266. package/templates/agent-workroom/web/package.json +21 -0
  267. package/templates/agent-workroom/web/src/App.tsx +345 -0
  268. package/templates/agent-workroom/web/src/lib/forge.ts +13 -0
  269. package/templates/agent-workroom/web/src/main.tsx +13 -0
  270. package/templates/agent-workroom/web/src/styles.css +545 -0
  271. package/templates/agent-workroom/web/tsconfig.json +27 -0
  272. package/templates/b2b-support-web/package.json +2 -0
  273. package/templates/b2b-support-web/tsconfig.json +4 -1
  274. package/templates/b2b-support-web/web/package.json +1 -1
  275. package/templates/minimal-web/package.json +2 -1
  276. package/templates/minimal-web/tsconfig.json +3 -1
  277. package/templates/minimal-web/web/package.json +2 -2
  278. package/src/forge/_generated/actionSubscriptions.json +0 -2
  279. package/src/forge/_generated/actionSubscriptions.ts +0 -10
  280. package/src/forge/_generated/agentAdapterManifest.json +0 -2
  281. package/src/forge/_generated/agentAdapterManifest.ts +0 -73
  282. package/src/forge/_generated/agentContract.json +0 -2
  283. package/src/forge/_generated/agentContract.ts +0 -7696
  284. package/src/forge/_generated/agentQuickstart.md +0 -32
  285. package/src/forge/_generated/aiContext.ts +0 -59
  286. package/src/forge/_generated/aiModels.json +0 -2
  287. package/src/forge/_generated/aiModels.ts +0 -35
  288. package/src/forge/_generated/aiProviders.json +0 -2
  289. package/src/forge/_generated/aiProviders.ts +0 -23
  290. package/src/forge/_generated/aiRegistry.json +0 -2
  291. package/src/forge/_generated/aiRegistry.ts +0 -29
  292. package/src/forge/_generated/api.json +0 -2
  293. package/src/forge/_generated/api.ts +0 -8
  294. package/src/forge/_generated/appGraph.json +0 -2
  295. package/src/forge/_generated/appGraph.ts +0 -14667
  296. package/src/forge/_generated/appMap.md +0 -35
  297. package/src/forge/_generated/artifactManifest.json +0 -2
  298. package/src/forge/_generated/artifactManifest.ts +0 -7
  299. package/src/forge/_generated/authClaims.json +0 -2
  300. package/src/forge/_generated/authClaims.ts +0 -13
  301. package/src/forge/_generated/authConfig.json +0 -2
  302. package/src/forge/_generated/authConfig.ts +0 -17
  303. package/src/forge/_generated/authContext.ts +0 -23
  304. package/src/forge/_generated/authRegistry.json +0 -2
  305. package/src/forge/_generated/authRegistry.ts +0 -25
  306. package/src/forge/_generated/buildInfo.json +0 -2
  307. package/src/forge/_generated/buildInfo.ts +0 -9
  308. package/src/forge/_generated/capabilityMap.json +0 -2
  309. package/src/forge/_generated/capabilityMap.md +0 -15
  310. package/src/forge/_generated/capabilityMap.ts +0 -17
  311. package/src/forge/_generated/client.ts +0 -282
  312. package/src/forge/_generated/clientApi.ts +0 -9
  313. package/src/forge/_generated/clientManifest.json +0 -2
  314. package/src/forge/_generated/clientManifest.ts +0 -39
  315. package/src/forge/_generated/clientTypes.ts +0 -78
  316. package/src/forge/_generated/configRegistry.json +0 -2
  317. package/src/forge/_generated/configRegistry.ts +0 -4
  318. package/src/forge/_generated/dataGraph.json +0 -2
  319. package/src/forge/_generated/dataGraph.ts +0 -8
  320. package/src/forge/_generated/db.json +0 -2
  321. package/src/forge/_generated/db.ts +0 -2
  322. package/src/forge/_generated/dbSecurityManifest.json +0 -2
  323. package/src/forge/_generated/dbSecurityManifest.ts +0 -15
  324. package/src/forge/_generated/dbSessionContext.json +0 -2
  325. package/src/forge/_generated/dbSessionContext.ts +0 -39
  326. package/src/forge/_generated/deployManifest.json +0 -2
  327. package/src/forge/_generated/deployManifest.ts +0 -14
  328. package/src/forge/_generated/devManifest.json +0 -2
  329. package/src/forge/_generated/devManifest.ts +0 -47
  330. package/src/forge/_generated/envSchema.json +0 -2
  331. package/src/forge/_generated/envSchema.ts +0 -59
  332. package/src/forge/_generated/frontendGraph.json +0 -2
  333. package/src/forge/_generated/frontendGraph.ts +0 -27
  334. package/src/forge/_generated/importGuards.json +0 -2
  335. package/src/forge/_generated/importGuards.ts +0 -686
  336. package/src/forge/_generated/index.ts +0 -67
  337. package/src/forge/_generated/liveProductionManifest.json +0 -2
  338. package/src/forge/_generated/liveProductionManifest.ts +0 -23
  339. package/src/forge/_generated/liveProtocol.json +0 -2
  340. package/src/forge/_generated/liveProtocol.ts +0 -21
  341. package/src/forge/_generated/liveQueryRegistry.json +0 -2
  342. package/src/forge/_generated/liveQueryRegistry.ts +0 -9
  343. package/src/forge/_generated/liveTransportConfig.json +0 -2
  344. package/src/forge/_generated/liveTransportConfig.ts +0 -19
  345. package/src/forge/_generated/makeRegistry.json +0 -2
  346. package/src/forge/_generated/makeRegistry.ts +0 -163
  347. package/src/forge/_generated/makeTemplates.json +0 -2
  348. package/src/forge/_generated/makeTemplates.ts +0 -61
  349. package/src/forge/_generated/mockMap.json +0 -2
  350. package/src/forge/_generated/mockMap.ts +0 -7
  351. package/src/forge/_generated/operationPlaybooks.md +0 -147
  352. package/src/forge/_generated/packageGraph.json +0 -2
  353. package/src/forge/_generated/packageGraph.ts +0 -245249
  354. package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
  355. package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
  356. package/src/forge/_generated/permissionMatrix.json +0 -2
  357. package/src/forge/_generated/permissionMatrix.ts +0 -7
  358. package/src/forge/_generated/policyRegistry.json +0 -2
  359. package/src/forge/_generated/policyRegistry.ts +0 -11
  360. package/src/forge/_generated/queryRegistry.json +0 -2
  361. package/src/forge/_generated/queryRegistry.ts +0 -9
  362. package/src/forge/_generated/react.d.ts +0 -22
  363. package/src/forge/_generated/react.ts +0 -29
  364. package/src/forge/_generated/reactManifest.json +0 -2
  365. package/src/forge/_generated/reactManifest.ts +0 -19
  366. package/src/forge/_generated/rlsPolicies.json +0 -2
  367. package/src/forge/_generated/rlsPolicies.sql +0 -34
  368. package/src/forge/_generated/rlsPolicies.ts +0 -6
  369. package/src/forge/_generated/runtimeGraph.json +0 -2
  370. package/src/forge/_generated/runtimeGraph.ts +0 -8
  371. package/src/forge/_generated/runtimeMatrix.json +0 -2
  372. package/src/forge/_generated/runtimeMatrix.ts +0 -327385
  373. package/src/forge/_generated/runtimeRegistry.ts +0 -2
  374. package/src/forge/_generated/runtimeRules.md +0 -79
  375. package/src/forge/_generated/secretRegistry.json +0 -2
  376. package/src/forge/_generated/secretRegistry.ts +0 -50
  377. package/src/forge/_generated/secretsContext.ts +0 -11
  378. package/src/forge/_generated/serverApi.ts +0 -10
  379. package/src/forge/_generated/sourceMapManifest.json +0 -2
  380. package/src/forge/_generated/sourceMapManifest.ts +0 -7
  381. package/src/forge/_generated/sqlPlan.json +0 -2
  382. package/src/forge/_generated/sqlPlan.ts +0 -88
  383. package/src/forge/_generated/subscriptionManifest.json +0 -2
  384. package/src/forge/_generated/subscriptionManifest.ts +0 -7
  385. package/src/forge/_generated/symbolicationManifest.json +0 -2
  386. package/src/forge/_generated/symbolicationManifest.ts +0 -17
  387. package/src/forge/_generated/telemetryRegistry.json +0 -2
  388. package/src/forge/_generated/telemetryRegistry.ts +0 -9
  389. package/src/forge/_generated/telemetrySinks.json +0 -2
  390. package/src/forge/_generated/telemetrySinks.ts +0 -11
  391. package/src/forge/_generated/tenantScope.json +0 -2
  392. package/src/forge/_generated/tenantScope.ts +0 -8
  393. package/src/forge/_generated/testGraph.json +0 -2
  394. package/src/forge/_generated/testGraph.ts +0 -3108
  395. package/src/forge/_generated/testPlanRegistry.json +0 -2
  396. package/src/forge/_generated/testPlanRegistry.ts +0 -33
  397. package/src/forge/_generated/uiRoutes.json +0 -2
  398. package/src/forge/_generated/uiRoutes.ts +0 -16
  399. package/src/forge/_generated/uiScenarios.json +0 -2
  400. package/src/forge/_generated/uiScenarios.ts +0 -30
  401. package/src/forge/_generated/uiTestManifest.json +0 -2
  402. package/src/forge/_generated/uiTestManifest.ts +0 -27
  403. package/src/forge/_generated/workflowRegistry.json +0 -2
  404. package/src/forge/_generated/workflowRegistry.ts +0 -9
  405. package/src/forge/_generated/workflowSubscriptions.json +0 -2
  406. package/src/forge/_generated/workflowSubscriptions.ts +0 -10
@@ -1,8 +1,10 @@
1
1
  import { spawn } from "node:child_process";
2
+ import { createServer as createNetServer } from "node:net";
2
3
  import { run } from "../compiler/orchestrator/run.ts";
3
- import { join } from "node:path";
4
+ import { basename, join } from "node:path";
4
5
  import { nodeFileSystem } from "../compiler/fs/index.ts";
5
6
  import { stripDeterministicHeader } from "../compiler/primitives/header.ts";
7
+ import { hashStable } from "../compiler/primitives/hash.ts";
6
8
  import { GENERATED_DIR } from "../compiler/emitter/constants.ts";
7
9
  import type { FrontendGraph } from "../compiler/types/frontend-graph.ts";
8
10
  import { detectPackageManager } from "../compiler/package-manager/detect.ts";
@@ -12,6 +14,12 @@ import {
12
14
  formatDevConsoleJson,
13
15
  runDevConsoleCycle,
14
16
  } from "../dev-console/cycle.ts";
17
+ import type {
18
+ DevConsoleAgentContext,
19
+ DevConsoleCycle,
20
+ DevConsoleGeneratedSummary,
21
+ DevConsoleSummary,
22
+ } from "../dev-console/types.ts";
15
23
  import {
16
24
  resolveDevHost,
17
25
  resolveDevPort,
@@ -19,6 +27,8 @@ import {
19
27
  } from "../dev/server.ts";
20
28
  import { startDevWatch } from "../dev/watch.ts";
21
29
  import type { DevServerHandle } from "../dev/types.ts";
30
+ import { createAmbientDeltaRecorder } from "../delta/index.ts";
31
+ import { resetCompileSessions } from "../compiler/orchestrator/session.ts";
22
32
 
23
33
  export interface DevCommandOptions {
24
34
  workspaceRoot: string;
@@ -29,7 +39,7 @@ export interface DevCommandOptions {
29
39
  once?: boolean;
30
40
  watch: boolean;
31
41
  json: boolean;
32
- db: "pglite" | "postgres" | "none";
42
+ db: "memory" | "pglite" | "postgres" | "none";
33
43
  databaseUrl?: string;
34
44
  worker: boolean;
35
45
  withWeb?: boolean;
@@ -41,6 +51,7 @@ export interface DevCommandOptions {
41
51
  envFile?: string;
42
52
  mode?: "dev" | "serve";
43
53
  allowDevAuth?: boolean;
54
+ skipStartupConsole?: boolean;
44
55
  }
45
56
 
46
57
  export interface DevCommandResult {
@@ -49,9 +60,30 @@ export interface DevCommandResult {
49
60
  exitCode: 0 | 1;
50
61
  }
51
62
 
63
+ export interface DevGenerateResult {
64
+ ok: boolean;
65
+ changed: string[];
66
+ unchanged: string[];
67
+ diagnostics: Array<{ severity: string; code: string; message: string }>;
68
+ exitCode: 0 | 1;
69
+ }
70
+
71
+ interface DevStartupGeneratedEvidence {
72
+ ok: boolean;
73
+ state: "fresh" | "regenerated" | "stale-risk";
74
+ changedFiles: number;
75
+ sampleChanged: string[];
76
+ hiddenChanged: number;
77
+ message: string;
78
+ command: string;
79
+ checkCommand: string;
80
+ }
81
+
52
82
  interface WebDevServerHandle {
53
83
  url: string;
54
84
  port: number;
85
+ requestedPort?: number;
86
+ autoPortSelected: boolean;
55
87
  command: string[];
56
88
  stop: () => void;
57
89
  }
@@ -70,14 +102,25 @@ interface DevStartupSummary {
70
102
  web: null | {
71
103
  url: string;
72
104
  port: number;
105
+ requestedPort?: number;
106
+ autoPortSelected: boolean;
73
107
  command: string[];
74
108
  framework: string;
75
109
  routes: string[];
76
110
  bridgeFiles: string[];
77
111
  apiUrlEnv?: string;
78
112
  };
113
+ preview: {
114
+ studioUrl?: string;
115
+ targetAppUrl: string;
116
+ targetAppPort: number;
117
+ isStudioSelfPreview: boolean;
118
+ note: string;
119
+ };
79
120
  watch: {
80
121
  enabled: boolean;
122
+ autoGenerate: boolean;
123
+ reloadsRuntime: boolean;
81
124
  };
82
125
  runtime: {
83
126
  routes: Array<{ method: string; path: string; purpose: string }>;
@@ -90,6 +133,20 @@ interface DevStartupSummary {
90
133
  bridgeFiles: string[];
91
134
  diagnostics: number;
92
135
  };
136
+ generated: {
137
+ state: "fresh" | "regenerated" | "stale-risk";
138
+ generatorVersion?: string;
139
+ inputHash?: string;
140
+ buildInfoHash?: string;
141
+ runtimeLoadedHash?: string;
142
+ runtimeStaleRisk: boolean;
143
+ changedFiles: number;
144
+ sampleChanged: string[];
145
+ hiddenChanged: number;
146
+ command: string;
147
+ checkCommand: string;
148
+ message: string;
149
+ };
93
150
  next: {
94
151
  browserUrl: string;
95
152
  apiIndex: string;
@@ -99,6 +156,132 @@ interface DevStartupSummary {
99
156
  pid: number;
100
157
  }
101
158
 
159
+ function nextPreviewPort(webUrl?: string): number {
160
+ if (!webUrl) {
161
+ return 5174;
162
+ }
163
+ try {
164
+ const parsed = new URL(webUrl);
165
+ const port = parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80;
166
+ return Number.isFinite(port) && port > 0 ? port + 1 : 5174;
167
+ } catch {
168
+ return 5174;
169
+ }
170
+ }
171
+
172
+ function isForgeStudioWorkspace(workspaceRoot: string): boolean {
173
+ return basename(workspaceRoot).toLowerCase() === "forge-studio";
174
+ }
175
+
176
+ function previewSummaryFor(input: {
177
+ workspaceRoot: string;
178
+ host: string;
179
+ webUrl?: string;
180
+ }): DevStartupSummary["preview"] {
181
+ if (input.webUrl && !isForgeStudioWorkspace(input.workspaceRoot)) {
182
+ const parsed = new URL(input.webUrl);
183
+ const targetAppPort = parsed.port ? Number(parsed.port) : parsed.protocol === "https:" ? 443 : 80;
184
+ return {
185
+ targetAppUrl: input.webUrl,
186
+ targetAppPort,
187
+ isStudioSelfPreview: false,
188
+ note: `Web app preview is running at ${input.webUrl}.`,
189
+ };
190
+ }
191
+
192
+ const targetAppPort = nextPreviewPort(input.webUrl);
193
+ const targetAppUrl = `http://${input.host}:${targetAppPort}`;
194
+ const isStudioSelfPreview = Boolean(input.webUrl && input.webUrl === targetAppUrl);
195
+ return {
196
+ ...(input.webUrl ? { studioUrl: input.webUrl } : {}),
197
+ targetAppUrl,
198
+ targetAppPort,
199
+ isStudioSelfPreview,
200
+ note: input.webUrl
201
+ ? `Use ${targetAppUrl} for the app being built when ${input.webUrl} is Forge Studio itself.`
202
+ : `No web app was detected; ${targetAppUrl} is the default target app preview URL for Studio attach flows.`,
203
+ };
204
+ }
205
+
206
+ async function isPortAvailable(host: string, port: number): Promise<boolean> {
207
+ if (port === 0) {
208
+ return true;
209
+ }
210
+ const server = createNetServer();
211
+ return new Promise<boolean>((resolve) => {
212
+ const cleanup = () => {
213
+ server.removeAllListeners();
214
+ };
215
+ server.once("error", () => {
216
+ cleanup();
217
+ resolve(false);
218
+ });
219
+ server.listen(port, host, () => {
220
+ server.close(() => {
221
+ cleanup();
222
+ resolve(true);
223
+ });
224
+ });
225
+ });
226
+ }
227
+
228
+ export async function resolveAvailableWebPort(input: {
229
+ host: string;
230
+ preferredPort: number;
231
+ maxAttempts?: number;
232
+ }): Promise<{ port: number; requestedPort?: number; autoPortSelected: boolean }> {
233
+ const attempts = input.maxAttempts ?? 20;
234
+ const start = Math.max(0, Math.floor(input.preferredPort));
235
+ if (start === 0) {
236
+ return { port: 0, autoPortSelected: false };
237
+ }
238
+
239
+ for (let offset = 0; offset < attempts; offset += 1) {
240
+ const candidate = start + offset;
241
+ if (await isPortAvailable(input.host, candidate)) {
242
+ return {
243
+ port: candidate,
244
+ ...(candidate !== start ? { requestedPort: start } : {}),
245
+ autoPortSelected: candidate !== start,
246
+ };
247
+ }
248
+ }
249
+
250
+ return {
251
+ port: 0,
252
+ requestedPort: start,
253
+ autoPortSelected: true,
254
+ };
255
+ }
256
+
257
+ export interface DevWatchGenerateFailureEvent {
258
+ schemaVersion: "0.1.0";
259
+ event: "dev.generate_failed";
260
+ ok: false;
261
+ changedFiles: number;
262
+ changedPaths: string[];
263
+ generated: DevConsoleGeneratedSummary;
264
+ diagnostics: DevGenerateResult["diagnostics"];
265
+ nextActions: string[];
266
+ }
267
+
268
+ export interface DevWatchReloadEvent {
269
+ schemaVersion: "0.1.0";
270
+ event: "dev.reload";
271
+ ok: boolean;
272
+ changedFiles: number;
273
+ changedPaths: string[];
274
+ migrated: boolean;
275
+ routes: number;
276
+ runtimeEntries: number;
277
+ worker: unknown;
278
+ diagnostics: DevGenerateResult["diagnostics"];
279
+ generated: DevConsoleGeneratedSummary;
280
+ preview: DevConsoleSummary["preview"];
281
+ agentContext: DevConsoleAgentContext;
282
+ nextActions: string[];
283
+ }
284
+
102
285
  function readGeneratedJson<T>(workspaceRoot: string, relative: string): T | null {
103
286
  const absolute = join(workspaceRoot, relative);
104
287
  if (!nodeFileSystem.exists(absolute)) {
@@ -175,6 +358,8 @@ function startWebDevServer(input: {
175
358
  workspaceRoot: string;
176
359
  host: string;
177
360
  port: number;
361
+ requestedPort?: number;
362
+ autoPortSelected?: boolean;
178
363
  apiUrl: string;
179
364
  json: boolean;
180
365
  }): WebDevServerHandle | null {
@@ -209,6 +394,8 @@ function startWebDevServer(input: {
209
394
  return {
210
395
  url: `http://${input.host}:${input.port}`,
211
396
  port: input.port,
397
+ ...(input.requestedPort !== undefined ? { requestedPort: input.requestedPort } : {}),
398
+ autoPortSelected: input.autoPortSelected ?? false,
212
399
  command,
213
400
  stop: () => {
214
401
  try {
@@ -225,15 +412,28 @@ function buildStartupSummary(input: {
225
412
  handle: DevServerHandle;
226
413
  web?: WebDevServerHandle | null;
227
414
  watch: boolean;
415
+ generated?: DevStartupGeneratedEvidence;
228
416
  }): DevStartupSummary {
229
417
  const frontend = readGeneratedJson<FrontendGraph>(
230
418
  input.workspaceRoot,
231
419
  `${GENERATED_DIR}/frontendGraph.json`,
232
420
  );
421
+ const buildInfo = readGeneratedJson<{ generatorVersion?: string; inputHash?: string }>(
422
+ input.workspaceRoot,
423
+ `${GENERATED_DIR}/buildInfo.json`,
424
+ );
425
+ const buildInfoRaw = nodeFileSystem.exists(join(input.workspaceRoot, `${GENERATED_DIR}/buildInfo.json`))
426
+ ? stripDeterministicHeader(nodeFileSystem.readText(join(input.workspaceRoot, `${GENERATED_DIR}/buildInfo.json`)) ?? "")
427
+ : "";
233
428
  const bindings = frontend
234
429
  ? [...new Set(frontend.clientBindings.map((binding) => `${binding.kind}:${binding.name}`))].sort()
235
430
  : [];
236
431
  const browserUrl = input.web?.url ?? input.handle.url;
432
+ const preview = previewSummaryFor({
433
+ workspaceRoot: input.workspaceRoot,
434
+ host: input.handle.host,
435
+ ...(input.web?.url ? { webUrl: input.web.url } : {}),
436
+ });
237
437
  return {
238
438
  schemaVersion: "0.1.0",
239
439
  ok: true,
@@ -249,6 +449,8 @@ function buildStartupSummary(input: {
249
449
  ? {
250
450
  url: input.web.url,
251
451
  port: input.web.port,
452
+ ...(input.web.requestedPort !== undefined ? { requestedPort: input.web.requestedPort } : {}),
453
+ autoPortSelected: input.web.autoPortSelected,
252
454
  command: input.web.command,
253
455
  framework: frontend?.framework ?? "unknown",
254
456
  routes: frontend?.routes.map((route) => route.path) ?? [],
@@ -256,8 +458,11 @@ function buildStartupSummary(input: {
256
458
  ...(frontend?.dev?.apiUrlEnv ? { apiUrlEnv: frontend.dev.apiUrlEnv } : {}),
257
459
  }
258
460
  : null,
461
+ preview,
259
462
  watch: {
260
463
  enabled: input.watch,
464
+ autoGenerate: input.watch,
465
+ reloadsRuntime: input.watch,
261
466
  },
262
467
  runtime: {
263
468
  routes: input.handle.routes.map((route) => ({
@@ -274,10 +479,24 @@ function buildStartupSummary(input: {
274
479
  bridgeFiles: frontend?.bridgeFiles ?? [],
275
480
  diagnostics: frontend?.diagnostics.length ?? 0,
276
481
  },
482
+ generated: {
483
+ state: input.generated?.state ?? (buildInfoRaw ? "fresh" : "stale-risk"),
484
+ ...(buildInfo?.generatorVersion ? { generatorVersion: buildInfo.generatorVersion } : {}),
485
+ ...(buildInfo?.inputHash ? { inputHash: buildInfo.inputHash } : {}),
486
+ ...(buildInfoRaw ? { buildInfoHash: hashStable(buildInfoRaw) } : {}),
487
+ ...(buildInfoRaw ? { runtimeLoadedHash: hashStable(buildInfoRaw) } : {}),
488
+ runtimeStaleRisk: !buildInfoRaw || input.generated?.state === "stale-risk",
489
+ changedFiles: input.generated?.changedFiles ?? 0,
490
+ sampleChanged: input.generated?.sampleChanged ?? [],
491
+ hiddenChanged: input.generated?.hiddenChanged ?? 0,
492
+ command: input.generated?.command ?? "forge generate",
493
+ checkCommand: input.generated?.checkCommand ?? "forge generate --check --json",
494
+ message: input.generated?.message ?? (buildInfoRaw ? "generated artifacts are loaded" : "generated build info is missing"),
495
+ },
277
496
  next: {
278
497
  browserUrl,
279
498
  apiIndex: input.handle.url,
280
- inspect: "forge inspect all --json",
499
+ inspect: "forge inspect summary --json",
281
500
  verify: "forge dev --once --json",
282
501
  },
283
502
  pid: process.pid,
@@ -295,11 +514,18 @@ function printStartupHuman(summary: DevStartupSummary): void {
295
514
  lines.push(` DB: ${summary.api.db.kind} ${summary.api.db.connected ? "connected" : "not connected"}`);
296
515
  lines.push(` Worker: ${summary.api.worker}`);
297
516
  lines.push(` Watch: ${summary.watch.enabled ? "on" : "off"}`);
517
+ lines.push(` Auto-generate: ${summary.watch.autoGenerate ? "on" : "off"}`);
518
+ lines.push(` Generated: ${summary.generated.state}${summary.generated.changedFiles > 0 ? ` (${summary.generated.changedFiles} changed)` : ""}${summary.generated.runtimeStaleRisk ? " (stale risk)" : ""}`);
519
+ lines.push(` Generated note: ${summary.generated.message}`);
520
+ lines.push(` Generated check: ${summary.generated.checkCommand}`);
298
521
  lines.push("");
299
522
 
300
523
  lines.push("Web app");
301
524
  if (summary.web) {
302
525
  lines.push(` URL: ${summary.web.url}`);
526
+ if (summary.web.autoPortSelected && summary.web.requestedPort) {
527
+ lines.push(` Requested port: ${summary.web.requestedPort} (busy; selected ${summary.web.port})`);
528
+ }
303
529
  lines.push(` Framework: ${summary.web.framework}`);
304
530
  lines.push(` API env: ${summary.web.apiUrlEnv ?? "unknown"}=${summary.api.url}`);
305
531
  lines.push(` Bridge: ${summary.web.bridgeFiles.length > 0 ? summary.web.bridgeFiles.join(", ") : "missing"}`);
@@ -325,6 +551,15 @@ function printStartupHuman(summary: DevStartupSummary): void {
325
551
  lines.push("Agent checks");
326
552
  lines.push(` ${summary.next.verify}`);
327
553
  lines.push(` ${summary.next.inspect}`);
554
+ lines.push(" forge changed --json");
555
+ lines.push("");
556
+
557
+ lines.push("Preview");
558
+ lines.push(` Target app: ${summary.preview.targetAppUrl}`);
559
+ if (summary.preview.studioUrl) {
560
+ lines.push(` Studio: ${summary.preview.studioUrl}`);
561
+ }
562
+ lines.push(` Note: ${summary.preview.note}`);
328
563
  lines.push("");
329
564
 
330
565
  process.stdout.write(`${lines.join("\n")}\n`);
@@ -350,6 +585,122 @@ function openBrowser(url: string): void {
350
585
  }
351
586
  }
352
587
 
588
+ export async function ensureGeneratedForDev(workspaceRoot: string): Promise<DevGenerateResult> {
589
+ resetCompileSessions();
590
+ const result = await run({
591
+ workspaceRoot,
592
+ check: false,
593
+ dryRun: false,
594
+ json: false,
595
+ concurrency: 4,
596
+ });
597
+ return {
598
+ ok: result.exitCode === 0,
599
+ changed: result.changed,
600
+ unchanged: result.unchanged,
601
+ diagnostics: [...result.errors, ...result.warnings],
602
+ exitCode: result.exitCode,
603
+ };
604
+ }
605
+
606
+ export function generatedEvidenceFromCycle(cycle: DevConsoleCycle): DevStartupGeneratedEvidence {
607
+ const phase = cycle.phases.find((item) => item.name === "generated");
608
+ const changedFiles = Number(phase?.details?.changed ?? 0);
609
+ const sampleChanged = Array.isArray(phase?.details?.sampleChanged)
610
+ ? phase.details.sampleChanged.filter((item): item is string => typeof item === "string")
611
+ : [];
612
+ const hiddenChanged = Number(phase?.details?.hiddenChanged ?? 0);
613
+ return {
614
+ ok: phase?.ok === true,
615
+ state: phase?.ok === true
616
+ ? changedFiles > 0 ? "regenerated" : "fresh"
617
+ : "stale-risk",
618
+ changedFiles,
619
+ sampleChanged,
620
+ hiddenChanged,
621
+ message: phase?.message ?? "generated phase did not report a message",
622
+ command: "forge generate",
623
+ checkCommand: "forge generate --check --json",
624
+ };
625
+ }
626
+
627
+ export function generatedEvidenceFromGenerateResult(result: DevGenerateResult): DevStartupGeneratedEvidence {
628
+ return {
629
+ ok: result.ok,
630
+ state: result.ok ? result.changed.length > 0 ? "regenerated" : "fresh" : "stale-risk",
631
+ changedFiles: result.changed.length,
632
+ sampleChanged: result.changed.slice(0, 12),
633
+ hiddenChanged: Math.max(0, result.changed.length - 12),
634
+ message: result.ok
635
+ ? result.changed.length > 0
636
+ ? `regenerated ${result.changed.length} generated artifacts`
637
+ : "generated artifacts are up to date"
638
+ : "generated artifacts could not be regenerated",
639
+ command: "forge generate",
640
+ checkCommand: "forge generate --check --json",
641
+ };
642
+ }
643
+
644
+ function printDevGenerateFailure(result: DevGenerateResult, json: boolean): void {
645
+ if (json) {
646
+ process.stdout.write(`${JSON.stringify({
647
+ ok: false,
648
+ phase: "generated",
649
+ diagnostics: result.diagnostics,
650
+ exitCode: 1,
651
+ })}\n`);
652
+ return;
653
+ }
654
+ console.error("error: forge dev could not regenerate generated artifacts");
655
+ for (const diagnostic of result.diagnostics.filter((item) => item.severity === "error").slice(0, 5)) {
656
+ console.error(`error ${diagnostic.code}: ${diagnostic.message}`);
657
+ }
658
+ }
659
+
660
+ export function buildDevWatchGenerateFailureEvent(input: {
661
+ changedCount: number;
662
+ changedPaths: string[];
663
+ result: DevGenerateResult;
664
+ }): DevWatchGenerateFailureEvent {
665
+ return {
666
+ schemaVersion: "0.1.0",
667
+ event: "dev.generate_failed",
668
+ ok: false,
669
+ changedFiles: input.changedCount,
670
+ changedPaths: input.changedPaths,
671
+ generated: generatedEvidenceFromGenerateResult(input.result),
672
+ diagnostics: input.result.diagnostics,
673
+ nextActions: ["forge dev --once --json", "forge check --json"],
674
+ };
675
+ }
676
+
677
+ export function buildDevWatchReloadEvent(input: {
678
+ changedCount: number;
679
+ changedPaths: string[];
680
+ generated: DevGenerateResult;
681
+ reload: Awaited<ReturnType<DevServerHandle["reload"]>>;
682
+ cycle: DevConsoleCycle;
683
+ }): DevWatchReloadEvent {
684
+ return {
685
+ schemaVersion: "0.1.0",
686
+ event: "dev.reload",
687
+ ok: input.reload.ok,
688
+ changedFiles: input.changedCount,
689
+ changedPaths: input.changedPaths,
690
+ migrated: input.reload.migrated,
691
+ routes: input.reload.routes,
692
+ runtimeEntries: input.reload.runtimeEntries,
693
+ worker: input.reload.worker,
694
+ diagnostics: input.reload.diagnostics,
695
+ generated: generatedEvidenceFromGenerateResult(input.generated),
696
+ preview: input.cycle.summary.preview,
697
+ agentContext: input.cycle.summary.agentContext,
698
+ nextActions: input.reload.ok
699
+ ? input.cycle.summary.agentContext.recommendedCommands
700
+ : ["forge dev --once --json", "forge check --json"],
701
+ };
702
+ }
703
+
353
704
  export async function runDevCommand(
354
705
  options: DevCommandOptions,
355
706
  ): Promise<DevCommandResult> {
@@ -368,22 +719,42 @@ export async function runDevCommand(
368
719
 
369
720
  const host = resolveDevHost(options.host);
370
721
  const port = resolveDevPort(options.port);
371
- const webPort = options.webPort ?? detectDefaultWebPort(workspaceRoot);
722
+ const requestedWebPort = options.webPort ?? detectDefaultWebPort(workspaceRoot);
372
723
  const shouldStartWeb =
373
724
  options.webOnly === true ||
374
725
  (options.withWeb !== false && !options.apiOnly && hasWebApp(workspaceRoot));
726
+ const webPortSelection = shouldStartWeb
727
+ ? await resolveAvailableWebPort({ host, preferredPort: requestedWebPort })
728
+ : { port: requestedWebPort, autoPortSelected: false };
729
+ const webPort = webPortSelection.port;
375
730
  const webUrl = shouldStartWeb ? `http://${host}:${webPort}` : undefined;
731
+ let startupGenerated: DevStartupGeneratedEvidence | undefined;
376
732
 
377
- const startupCycle = await runDevConsoleCycle({
378
- workspaceRoot,
379
- mode: "startup",
380
- strictSecrets: false,
381
- includeImpact: true,
382
- });
383
- if (options.json) {
384
- process.stdout.write(formatDevConsoleJson(startupCycle));
733
+ if (!options.skipStartupConsole) {
734
+ const startupCycle = await runDevConsoleCycle({
735
+ workspaceRoot,
736
+ mode: "startup",
737
+ strictSecrets: false,
738
+ includeImpact: true,
739
+ apiUrl: `http://${host}:${port}`,
740
+ ...(webUrl ? { webUrl } : {}),
741
+ });
742
+ if (options.json) {
743
+ process.stdout.write(formatDevConsoleJson(startupCycle));
744
+ } else {
745
+ process.stdout.write(formatDevConsoleHuman(startupCycle));
746
+ }
747
+ startupGenerated = generatedEvidenceFromCycle(startupCycle);
748
+ if (startupCycle.exitCode !== 0) {
749
+ return { exitCode: 1 };
750
+ }
385
751
  } else {
386
- process.stdout.write(formatDevConsoleHuman(startupCycle));
752
+ const generated = await ensureGeneratedForDev(workspaceRoot);
753
+ startupGenerated = generatedEvidenceFromGenerateResult(generated);
754
+ if (!generated.ok) {
755
+ printDevGenerateFailure(generated, options.json);
756
+ return { exitCode: 1 };
757
+ }
387
758
  }
388
759
 
389
760
  if (options.webOnly) {
@@ -392,6 +763,8 @@ export async function runDevCommand(
392
763
  workspaceRoot,
393
764
  host,
394
765
  port: webPort,
766
+ requestedPort: webPortSelection.requestedPort,
767
+ autoPortSelected: webPortSelection.autoPortSelected,
395
768
  apiUrl,
396
769
  json: options.json,
397
770
  });
@@ -413,6 +786,8 @@ export async function runDevCommand(
413
786
  web: {
414
787
  url: webHandle.url,
415
788
  port: webHandle.port,
789
+ ...(webHandle.requestedPort !== undefined ? { requestedPort: webHandle.requestedPort } : {}),
790
+ autoPortSelected: webHandle.autoPortSelected,
416
791
  command: webHandle.command,
417
792
  },
418
793
  api: {
@@ -440,6 +815,8 @@ export async function runDevCommand(
440
815
  return { web: webHandle, exitCode: 0 };
441
816
  }
442
817
 
818
+ const deltaRecorder = await createAmbientDeltaRecorder(workspaceRoot, "forge-dev", "forge dev");
819
+
443
820
  let handle: DevServerHandle;
444
821
  try {
445
822
  handle = await startDevServer({
@@ -457,13 +834,38 @@ export async function runDevCommand(
457
834
  mode: options.mode,
458
835
  allowDevAuth: options.allowDevAuth,
459
836
  webUrl,
837
+ deltaRecorder,
460
838
  });
461
839
  } catch (error) {
462
- const message =
840
+ await deltaRecorder.close("forge dev failed to start");
841
+ const rawMessage =
463
842
  error instanceof Error ? error.message : "failed to start dev server";
843
+ const lowerMessage = rawMessage.toLowerCase();
844
+ const busy =
845
+ lowerMessage.includes("eaddrinuse") ||
846
+ lowerMessage.includes("address already in use") ||
847
+ /port\s+\d+\s+.*in use/i.test(rawMessage);
848
+ const message = busy
849
+ ? `${rawMessage}. Port ${port} appears busy; stop the existing process or rerun with --port 0 / --port <free-port>.`
850
+ : rawMessage;
464
851
  if (options.json) {
465
852
  process.stdout.write(
466
- `${JSON.stringify({ ok: false, error: message, exitCode: 1 })}\n`,
853
+ `${JSON.stringify({
854
+ ok: false,
855
+ error: message,
856
+ failureKind: busy ? "port_busy" : "dev_start_failed",
857
+ busy: busy
858
+ ? {
859
+ port,
860
+ host,
861
+ suggestedCommands: [
862
+ `forge dev --port 0${options.webPort ? ` --web-port ${options.webPort}` : ""} --json`,
863
+ "forge doctor windows --json",
864
+ ],
865
+ }
866
+ : undefined,
867
+ exitCode: 1,
868
+ })}\n`,
467
869
  );
468
870
  } else {
469
871
  console.error(`error: ${message}`);
@@ -477,6 +879,8 @@ export async function runDevCommand(
477
879
  workspaceRoot,
478
880
  host,
479
881
  port: webPort,
882
+ requestedPort: webPortSelection.requestedPort,
883
+ autoPortSelected: webPortSelection.autoPortSelected,
480
884
  apiUrl: handle.url,
481
885
  json: options.json,
482
886
  });
@@ -486,6 +890,7 @@ export async function runDevCommand(
486
890
  handle,
487
891
  web: webHandle,
488
892
  watch: options.watch,
893
+ generated: startupGenerated,
489
894
  });
490
895
  if (options.json) {
491
896
  printStartupJson(startupSummary);
@@ -503,8 +908,17 @@ export async function runDevCommand(
503
908
  outboxWorkerHandle = handle.outboxWorker;
504
909
  }
505
910
 
911
+ const devConsoleUrlOptions = (): { apiUrl: string; webUrl?: string } => ({
912
+ apiUrl: handle.url,
913
+ ...(webHandle?.url ? { webUrl: webHandle.url } : {}),
914
+ });
915
+
506
916
  if (options.watch) {
507
- watchHandle = startDevWatch(workspaceRoot, async (changedCount) => {
917
+ watchHandle = startDevWatch(workspaceRoot, async (changedCount, changedPaths) => {
918
+ for (const changedPath of changedPaths) {
919
+ await deltaRecorder.recordFileChanged(changedPath);
920
+ }
921
+ resetCompileSessions();
508
922
  const result = await run({
509
923
  workspaceRoot,
510
924
  check: false,
@@ -514,39 +928,89 @@ export async function runDevCommand(
514
928
  });
515
929
 
516
930
  if (result.exitCode === 0) {
931
+ const reload = await handle.reload("watch");
932
+ const cycle = await runDevConsoleCycle({
933
+ workspaceRoot,
934
+ mode: "watch",
935
+ strictSecrets: false,
936
+ includeImpact: true,
937
+ ...devConsoleUrlOptions(),
938
+ });
517
939
  if (!options.json) {
518
940
  process.stdout.write(
519
- `[forge dev] regenerated (${changedCount} changed files)\n`,
941
+ `[forge dev] regenerated (${changedCount} changed files), ` +
942
+ `migrations ${reload.migrated ? "applied" : "skipped"}, ` +
943
+ `runtime ${reload.ok ? "reloaded" : "reload failed"}\n`,
520
944
  );
945
+ } else {
946
+ process.stdout.write(`${JSON.stringify(buildDevWatchReloadEvent({
947
+ changedCount,
948
+ changedPaths,
949
+ generated: {
950
+ ok: true,
951
+ changed: result.changed,
952
+ unchanged: result.unchanged,
953
+ diagnostics: [...result.errors, ...result.warnings],
954
+ exitCode: 0,
955
+ },
956
+ reload,
957
+ cycle,
958
+ }))}\n`);
521
959
  }
522
- } else if (!options.json) {
523
- for (const diagnostic of result.errors) {
524
- console.error(`error ${diagnostic.code}: ${diagnostic.message}`);
960
+ if (!reload.ok && !options.json) {
961
+ for (const diagnostic of reload.diagnostics.filter((item) => item.severity === "error")) {
962
+ console.error(`error ${diagnostic.code}: ${diagnostic.message}`);
963
+ }
964
+ }
965
+ if (options.json) {
966
+ process.stdout.write(formatDevConsoleJson(cycle));
967
+ } else {
968
+ process.stdout.write(formatDevConsoleHuman(cycle));
525
969
  }
526
- }
527
-
528
- const cycle = await runDevConsoleCycle({
529
- workspaceRoot,
530
- mode: "watch",
531
- strictSecrets: false,
532
- includeImpact: true,
533
- });
534
- if (options.json) {
535
- process.stdout.write(formatDevConsoleJson(cycle));
536
970
  } else {
537
- process.stdout.write(formatDevConsoleHuman(cycle));
971
+ if (options.json) {
972
+ process.stdout.write(`${JSON.stringify(buildDevWatchGenerateFailureEvent({
973
+ changedCount,
974
+ changedPaths,
975
+ result: {
976
+ ok: false,
977
+ changed: result.changed,
978
+ unchanged: result.unchanged,
979
+ diagnostics: [...result.errors, ...result.warnings],
980
+ exitCode: 1,
981
+ },
982
+ }))}\n`);
983
+ } else {
984
+ console.error(`[forge dev] regeneration failed after ${changedCount} changed files; runtime was not reloaded`);
985
+ for (const diagnostic of [...result.errors, ...result.warnings]) {
986
+ console.error(`${diagnostic.severity} ${diagnostic.code}: ${diagnostic.message}`);
987
+ }
988
+ }
989
+ const cycle = await runDevConsoleCycle({
990
+ workspaceRoot,
991
+ mode: "watch",
992
+ strictSecrets: false,
993
+ includeImpact: true,
994
+ ...devConsoleUrlOptions(),
995
+ });
996
+ if (options.json) {
997
+ process.stdout.write(formatDevConsoleJson(cycle));
998
+ } else {
999
+ process.stdout.write(formatDevConsoleHuman(cycle));
1000
+ }
538
1001
  }
539
1002
  });
540
1003
  }
541
1004
 
542
1005
  await new Promise<void>((resolve) => {
543
- const shutdown = () => {
544
- watchHandle?.stop();
545
- outboxWorkerHandle?.stop();
546
- webHandle?.stop();
547
- handle.stop();
548
- resolve();
549
- };
1006
+ const shutdown = () => {
1007
+ watchHandle?.stop();
1008
+ outboxWorkerHandle?.stop();
1009
+ webHandle?.stop();
1010
+ handle.stop();
1011
+ void deltaRecorder.close("forge dev stopped");
1012
+ resolve();
1013
+ };
550
1014
 
551
1015
  process.once("SIGINT", shutdown);
552
1016
  process.once("SIGTERM", shutdown);