forgeos 0.1.0-alpha.7 → 0.1.0-alpha.71

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 (598) hide show
  1. package/.agents/skills/forge-agent-fabric/SKILL.md +178 -0
  2. package/.agents/skills/forge-agent-fabric/references/managed-workflows.md +95 -0
  3. package/.agents/skills/forge-agent-fabric/references/repository-analysis.md +102 -0
  4. package/.agents/skills/forge-agent-fabric/scripts/fabric.mjs +46 -0
  5. package/.agents/skills/forge-agent-fabric/scripts/repository.mjs +44 -0
  6. package/.npmignore +10 -0
  7. package/AGENTS.md +142 -90
  8. package/CHANGELOG.md +870 -9
  9. package/LICENSE +21 -0
  10. package/README.md +121 -6
  11. package/adapters/go/README.md +23 -0
  12. package/adapters/go/go.mod +3 -0
  13. package/adapters/go/http.go +149 -0
  14. package/adapters/go/registry.go +234 -0
  15. package/adapters/go/types.go +136 -0
  16. package/adapters/java/README.md +68 -0
  17. package/adapters/java/pom.xml +47 -0
  18. package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
  19. package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
  20. package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
  21. package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
  22. package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
  23. package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
  24. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
  25. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
  26. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
  27. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
  28. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
  29. package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
  30. package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
  31. package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
  32. package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
  33. package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
  34. package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
  35. package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
  36. package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
  37. package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
  38. package/adapters/java-spring-boot-starter/README.md +32 -0
  39. package/adapters/java-spring-boot-starter/pom.xml +49 -0
  40. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
  41. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
  42. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
  43. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
  44. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
  45. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
  46. package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
  47. package/bin/forge.mjs +18 -0
  48. package/docs/agent-fabric-programs.md +151 -0
  49. package/docs/agent-fabric.md +582 -0
  50. package/docs/architecture/agent-fabric/PORTABLE_PROJECTS_PLAN.md +136 -0
  51. package/docs/architecture/agent-fabric/README.md +180 -0
  52. package/docs/architecture/agent-fabric/REPOSITORY_MANIFEST_PLAN.md +284 -0
  53. package/docs/architecture/agent-fabric/WORKFLOW_PROGRAM_V2_ADR.md +81 -0
  54. package/docs/architecture/agent-fabric/WORKFLOW_PROGRAM_V2_CORRECTIONS.md +39 -0
  55. package/docs/architecture/agent-fabric/WORKFLOW_PROGRAM_V2_CORRECTIONS_EVIDENCE.md +53 -0
  56. package/docs/architecture/agent-fabric/WORKFLOW_PROGRAM_V2_EVIDENCE.md +49 -0
  57. package/docs/architecture/agent-fabric/WORKFLOW_PROGRAM_V2_PLAN.md +590 -0
  58. package/docs/cair-protocol.md +103 -0
  59. package/docs/changelog.md +1070 -0
  60. package/docs/forge-protocol.md +189 -0
  61. package/docs/repository-agent-benchmark.md +179 -0
  62. package/docs/repository-analysis.md +139 -0
  63. package/docs/repository-context-quality.md +179 -0
  64. package/docs/repository-runtime-observation.md +82 -0
  65. package/examples/agent-fabric-v2/README.md +70 -0
  66. package/examples/agent-fabric-v2/item-child.workflow.ts +11 -0
  67. package/examples/agent-fabric-v2/make-request.mjs +4 -0
  68. package/examples/agent-fabric-v2/map-child.workflow.ts +10 -0
  69. package/examples/agent-fabric-v2/migrate.workflow.ts +35 -0
  70. package/examples/agent-fabric-v2/registry.example.json +256 -0
  71. package/examples/agent-fabric-v2/run-ui-fixture.ts +48 -0
  72. package/examples/agent-fabric-v2/ui-audit.workflow.ts +28 -0
  73. package/examples/agent-fabric-v2/ui-fixture-worker.cjs +36 -0
  74. package/examples/agent-fabric-v2/visual-population.json +13 -0
  75. package/examples/agent-fabric-v2/workers/check.mjs +2 -0
  76. package/examples/agent-fabric-v2/workers/common.mjs +7 -0
  77. package/examples/agent-fabric-v2/workers/discover.mjs +2 -0
  78. package/examples/agent-fabric-v2/workers/migrate.mjs +4 -0
  79. package/examples/agent-fabric-v2/workers/review.mjs +3 -0
  80. package/examples/go-billing/go.mod +7 -0
  81. package/examples/go-billing/main.go +120 -0
  82. package/examples/java-billing/pom.xml +60 -0
  83. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
  84. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
  85. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
  86. package/package.json +60 -5
  87. package/schemas/agent-fabric/v0.1/control-event.schema.json +281 -0
  88. package/schemas/forge-manifest.schema.json +389 -0
  89. package/scripts/field-test-forgeos.mjs +1021 -0
  90. package/scripts/install-agent-fabric-skill.mjs +105 -0
  91. package/src/forge/_generated/releaseManifest.json +1 -2
  92. package/src/forge/_generated/releaseManifest.ts +3 -3
  93. package/src/forge/agent-adapters/index.ts +1592 -125
  94. package/src/forge/agent-adapters/types.ts +225 -1
  95. package/src/forge/agent-fabric/adapter.ts +131 -0
  96. package/src/forge/agent-fabric/attached-snapshot.ts +55 -0
  97. package/src/forge/agent-fabric/attached-task-contract.ts +105 -0
  98. package/src/forge/agent-fabric/attached-task-service.ts +127 -0
  99. package/src/forge/agent-fabric/attached-task-store.ts +109 -0
  100. package/src/forge/agent-fabric/authority.ts +298 -0
  101. package/src/forge/agent-fabric/canonical.ts +135 -0
  102. package/src/forge/agent-fabric/codex-adversarial-review.ts +229 -0
  103. package/src/forge/agent-fabric/codex-sdk-worker.ts +203 -0
  104. package/src/forge/agent-fabric/conductor.ts +754 -0
  105. package/src/forge/agent-fabric/dictionary.ts +12 -0
  106. package/src/forge/agent-fabric/errors.ts +32 -0
  107. package/src/forge/agent-fabric/hardened-conductor.ts +485 -0
  108. package/src/forge/agent-fabric/hardened-reducer.ts +211 -0
  109. package/src/forge/agent-fabric/index.ts +48 -0
  110. package/src/forge/agent-fabric/journal.ts +116 -0
  111. package/src/forge/agent-fabric/local-adaptive-digest-worker.mjs +26 -0
  112. package/src/forge/agent-fabric/local-adaptive-harness.ts +343 -0
  113. package/src/forge/agent-fabric/local-adaptive-service.ts +398 -0
  114. package/src/forge/agent-fabric/local-adaptive-worker.ts +146 -0
  115. package/src/forge/agent-fabric/local-approval-window.ts +211 -0
  116. package/src/forge/agent-fabric/local-change-review-service.ts +374 -0
  117. package/src/forge/agent-fabric/local-coding-worker.ts +285 -0
  118. package/src/forge/agent-fabric/local-control-store.ts +239 -0
  119. package/src/forge/agent-fabric/local-effect-broker.ts +348 -0
  120. package/src/forge/agent-fabric/local-evolution-approval.ts +81 -0
  121. package/src/forge/agent-fabric/local-evolution-profile.ts +87 -0
  122. package/src/forge/agent-fabric/local-evolution-registry.ts +427 -0
  123. package/src/forge/agent-fabric/local-evolution-service.ts +200 -0
  124. package/src/forge/agent-fabric/local-intelligence.ts +281 -0
  125. package/src/forge/agent-fabric/local-paths.ts +18 -0
  126. package/src/forge/agent-fabric/local-task-contract.ts +203 -0
  127. package/src/forge/agent-fabric/local-task-inbox.ts +543 -0
  128. package/src/forge/agent-fabric/local-task-server.ts +374 -0
  129. package/src/forge/agent-fabric/local-task-service.ts +982 -0
  130. package/src/forge/agent-fabric/local-verification.ts +551 -0
  131. package/src/forge/agent-fabric/managed-environment.ts +205 -0
  132. package/src/forge/agent-fabric/managed-run-contract.ts +103 -0
  133. package/src/forge/agent-fabric/managed-run-service.ts +401 -0
  134. package/src/forge/agent-fabric/managed-run-store.ts +165 -0
  135. package/src/forge/agent-fabric/managed-workspace.ts +253 -0
  136. package/src/forge/agent-fabric/p0a.ts +104 -0
  137. package/src/forge/agent-fabric/p0b-model-adapter.ts +434 -0
  138. package/src/forge/agent-fabric/planning.ts +155 -0
  139. package/src/forge/agent-fabric/program-api.ts +37 -0
  140. package/src/forge/agent-fabric/program-contract.ts +283 -0
  141. package/src/forge/agent-fabric/program-dsl.ts +126 -0
  142. package/src/forge/agent-fabric/program-evidence.ts +22 -0
  143. package/src/forge/agent-fabric/program-image.ts +15 -0
  144. package/src/forge/agent-fabric/program-lock.ts +50 -0
  145. package/src/forge/agent-fabric/program-owner.ts +16 -0
  146. package/src/forge/agent-fabric/program-scheduler.ts +38 -0
  147. package/src/forge/agent-fabric/program-service.ts +793 -0
  148. package/src/forge/agent-fabric/program-store.ts +142 -0
  149. package/src/forge/agent-fabric/program-structure.ts +65 -0
  150. package/src/forge/agent-fabric/program-types.ts +110 -0
  151. package/src/forge/agent-fabric/program-view.ts +12 -0
  152. package/src/forge/agent-fabric/program-worker.ts +108 -0
  153. package/src/forge/agent-fabric/project-profile.ts +50 -0
  154. package/src/forge/agent-fabric/project-registry.ts +120 -0
  155. package/src/forge/agent-fabric/project-runtime.ts +85 -0
  156. package/src/forge/agent-fabric/readiness.ts +26 -0
  157. package/src/forge/agent-fabric/reducer.ts +863 -0
  158. package/src/forge/agent-fabric/repository-context.ts +83 -0
  159. package/src/forge/agent-fabric/resource-ledger.ts +266 -0
  160. package/src/forge/agent-fabric/serialized-local-adapter.ts +60 -0
  161. package/src/forge/agent-fabric/types.ts +424 -0
  162. package/src/forge/agent-fabric/validation.ts +500 -0
  163. package/src/forge/agent-fabric/workflow-engine.ts +218 -0
  164. package/src/forge/agent-fabric/workflow-task-actions.ts +104 -0
  165. package/src/forge/agent-fabric/workflow-templates.ts +11 -0
  166. package/src/forge/agent-memory/bridge.ts +1728 -0
  167. package/src/forge/agent-memory/context-pack.ts +284 -0
  168. package/src/forge/agent-memory/hook-runner.ts +331 -0
  169. package/src/forge/agent-memory/mcp.ts +597 -0
  170. package/src/forge/agent-memory/normalize.ts +498 -0
  171. package/src/forge/agent-memory/redaction.ts +103 -0
  172. package/src/forge/agent-memory/sources/claude-code.ts +51 -0
  173. package/src/forge/agent-memory/sources/codex-hook-runner.mjs +343 -0
  174. package/src/forge/agent-memory/sources/codex.ts +161 -0
  175. package/src/forge/agent-memory/sources/cursor.ts +35 -0
  176. package/src/forge/agent-memory/types.ts +215 -0
  177. package/src/forge/bench.ts +248 -0
  178. package/src/forge/brownfield-import/index.ts +896 -0
  179. package/src/forge/brownfield-import/types.ts +127 -0
  180. package/src/forge/cair/action-journal.ts +61 -0
  181. package/src/forge/cair/action-parser.ts +314 -0
  182. package/src/forge/cair/action-validator.ts +40 -0
  183. package/src/forge/cair/actions.ts +1818 -0
  184. package/src/forge/cair/format.ts +77 -0
  185. package/src/forge/cair/index.ts +118 -0
  186. package/src/forge/cair/query.ts +478 -0
  187. package/src/forge/cair/snapshot.ts +315 -0
  188. package/src/forge/cair/types.ts +255 -0
  189. package/src/forge/cli/adaptive.ts +51 -0
  190. package/src/forge/cli/ai.ts +16 -1
  191. package/src/forge/cli/auth.ts +459 -15
  192. package/src/forge/cli/authmd.ts +441 -0
  193. package/src/forge/cli/baseline.ts +112 -0
  194. package/src/forge/cli/build.ts +20 -4
  195. package/src/forge/cli/changed.ts +418 -0
  196. package/src/forge/cli/codex-app-server.ts +877 -0
  197. package/src/forge/cli/commands.ts +1859 -20
  198. package/src/forge/cli/db.ts +209 -2
  199. package/src/forge/cli/deploy.ts +1339 -0
  200. package/src/forge/cli/deps.ts +150 -23
  201. package/src/forge/cli/dev.ts +1137 -53
  202. package/src/forge/cli/docs.ts +265 -0
  203. package/src/forge/cli/doctor.ts +297 -0
  204. package/src/forge/cli/env-placeholders.ts +62 -0
  205. package/src/forge/cli/evolution.ts +40 -0
  206. package/src/forge/cli/fabric.ts +253 -0
  207. package/src/forge/cli/field-test.ts +568 -0
  208. package/src/forge/cli/golden-path.ts +487 -0
  209. package/src/forge/cli/handoff.ts +336 -0
  210. package/src/forge/cli/index.ts +1 -0
  211. package/src/forge/cli/last-run.ts +84 -0
  212. package/src/forge/cli/main.ts +196 -3
  213. package/src/forge/cli/new.ts +169 -17
  214. package/src/forge/cli/next-actions.ts +23 -0
  215. package/src/forge/cli/output.ts +398 -6
  216. package/src/forge/cli/parse.ts +1377 -38
  217. package/src/forge/cli/progress.ts +51 -0
  218. package/src/forge/cli/query.ts +32 -0
  219. package/src/forge/cli/release.ts +35 -11
  220. package/src/forge/cli/repository.ts +151 -0
  221. package/src/forge/cli/run.ts +42 -0
  222. package/src/forge/cli/secrets.ts +137 -1
  223. package/src/forge/cli/seed.ts +581 -0
  224. package/src/forge/cli/self-host.ts +56 -14
  225. package/src/forge/cli/studio.ts +2282 -0
  226. package/src/forge/cli/verify.ts +1458 -32
  227. package/src/forge/cli/windows.ts +23 -0
  228. package/src/forge/cli/workos.ts +3509 -0
  229. package/src/forge/compiler/agent-contract/build.ts +728 -64
  230. package/src/forge/compiler/agent-contract/types.ts +45 -2
  231. package/src/forge/compiler/ai-registry/parse.ts +71 -2
  232. package/src/forge/compiler/api-surface/build.ts +47 -0
  233. package/src/forge/compiler/app-graph/build.ts +112 -9
  234. package/src/forge/compiler/app-graph/extract.ts +107 -0
  235. package/src/forge/compiler/app-graph/module-graph.ts +73 -78
  236. package/src/forge/compiler/app-graph/parser.ts +24 -24
  237. package/src/forge/compiler/app-graph/profile.ts +26 -0
  238. package/src/forge/compiler/app-graph/tsconfig-hash.ts +41 -4
  239. package/src/forge/compiler/app-graph/versions.ts +1 -1
  240. package/src/forge/compiler/classifier/capabilities.ts +3 -2
  241. package/src/forge/compiler/classifier/classify.ts +32 -8
  242. package/src/forge/compiler/classifier/secrets.ts +3 -2
  243. package/src/forge/compiler/classifier/signals.ts +91 -1
  244. package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
  245. package/src/forge/compiler/client-sdk/render-client.ts +295 -13
  246. package/src/forge/compiler/data-graph/build.ts +29 -1
  247. package/src/forge/compiler/data-graph/parse.ts +20 -4
  248. package/src/forge/compiler/data-graph/sql/ddl.ts +144 -31
  249. package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
  250. package/src/forge/compiler/data-graph/sql/types.ts +1 -0
  251. package/src/forge/compiler/diagnostics/codes.ts +38 -0
  252. package/src/forge/compiler/diagnostics/create.ts +28 -2
  253. package/src/forge/compiler/diagnostics/index.ts +2 -0
  254. package/src/forge/compiler/emitter/barrel.ts +3 -0
  255. package/src/forge/compiler/emitter/render.ts +9 -0
  256. package/src/forge/compiler/external-manifest/registry.ts +217 -0
  257. package/src/forge/compiler/external-manifest/types.ts +91 -0
  258. package/src/forge/compiler/external-manifest/validate.ts +373 -0
  259. package/src/forge/compiler/frontend-graph/build.ts +142 -14
  260. package/src/forge/compiler/integration/add.ts +991 -33
  261. package/src/forge/compiler/integration/plan.ts +43 -12
  262. package/src/forge/compiler/integration/render.ts +29 -0
  263. package/src/forge/compiler/integration/snapshot.ts +2 -0
  264. package/src/forge/compiler/integration/templates/convex.ts +70 -0
  265. package/src/forge/compiler/integration/templates/index.ts +1 -0
  266. package/src/forge/compiler/integration/templates/render.ts +38 -0
  267. package/src/forge/compiler/integration/templates/types.ts +3 -0
  268. package/src/forge/compiler/integration/templates/workos.ts +1439 -0
  269. package/src/forge/compiler/make-registry/build.ts +8 -9
  270. package/src/forge/compiler/orchestrator/fast-check.ts +4 -0
  271. package/src/forge/compiler/orchestrator/generate-lock.ts +14 -3
  272. package/src/forge/compiler/orchestrator/manifest.ts +1 -1
  273. package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
  274. package/src/forge/compiler/orchestrator/plan.ts +249 -10
  275. package/src/forge/compiler/orchestrator/profile.ts +65 -0
  276. package/src/forge/compiler/orchestrator/run.ts +101 -33
  277. package/src/forge/compiler/orchestrator/serialize.ts +33 -8
  278. package/src/forge/compiler/orchestrator/types.ts +1 -1
  279. package/src/forge/compiler/package-graph/compiler.ts +3 -1
  280. package/src/forge/compiler/package-graph/constants.ts +1 -1
  281. package/src/forge/compiler/package-graph/dts-extractor.ts +14 -1
  282. package/src/forge/compiler/package-manager/adapter.ts +4 -1
  283. package/src/forge/compiler/package-manager/commands.ts +4 -0
  284. package/src/forge/compiler/package-manager/executor.ts +30 -1
  285. package/src/forge/compiler/package-manager/version.ts +36 -12
  286. package/src/forge/compiler/package-upgrades/planner.ts +155 -18
  287. package/src/forge/compiler/package-upgrades/types.ts +2 -0
  288. package/src/forge/compiler/policy-registry/build.ts +47 -2
  289. package/src/forge/compiler/policy-registry/parse.ts +32 -2
  290. package/src/forge/compiler/recipes/definitions.ts +63 -0
  291. package/src/forge/compiler/recipes/index.ts +2 -0
  292. package/src/forge/compiler/recipes/profiles.ts +81 -0
  293. package/src/forge/compiler/recipes/registry.ts +6 -0
  294. package/src/forge/compiler/test-graph/build.ts +11 -3
  295. package/src/forge/compiler/types/app-graph.ts +8 -2
  296. package/src/forge/compiler/types/cli.ts +76 -1
  297. package/src/forge/compiler/types/dev-manifest.ts +4 -0
  298. package/src/forge/compiler/types/emit.ts +2 -0
  299. package/src/forge/compiler/types/frontend-graph.ts +2 -2
  300. package/src/forge/compiler/types/integration.ts +1 -0
  301. package/src/forge/compiler/types/package-graph.ts +6 -0
  302. package/src/forge/compiler/types/policy-registry.ts +3 -1
  303. package/src/forge/delta/broker-runner.mjs +5 -0
  304. package/src/forge/delta/broker.ts +429 -0
  305. package/src/forge/delta/classifier.ts +52 -0
  306. package/src/forge/delta/explain.ts +238 -0
  307. package/src/forge/delta/git-observer.ts +67 -0
  308. package/src/forge/delta/ids.ts +44 -0
  309. package/src/forge/delta/index.ts +25 -0
  310. package/src/forge/delta/process-identity.ts +37 -0
  311. package/src/forge/delta/queue-status.ts +190 -0
  312. package/src/forge/delta/recorder.ts +540 -0
  313. package/src/forge/delta/redaction.ts +50 -0
  314. package/src/forge/delta/schema.ts +240 -0
  315. package/src/forge/delta/session.ts +142 -0
  316. package/src/forge/delta/status.ts +1139 -0
  317. package/src/forge/delta/store.ts +3329 -0
  318. package/src/forge/delta/timeline.ts +178 -0
  319. package/src/forge/dev/server.ts +1061 -22
  320. package/src/forge/dev/types.ts +15 -1
  321. package/src/forge/dev/watch.ts +17 -7
  322. package/src/forge/dev-console/cycle.ts +285 -33
  323. package/src/forge/dev-console/types.ts +47 -1
  324. package/src/forge/impact/index.ts +222 -30
  325. package/src/forge/impact/types.ts +33 -1
  326. package/src/forge/intent/index.ts +35 -16
  327. package/src/forge/make/fields.ts +26 -0
  328. package/src/forge/make/index.ts +42 -6
  329. package/src/forge/make/templates.ts +211 -4
  330. package/src/forge/make/types.ts +2 -1
  331. package/src/forge/policy.ts +1 -1
  332. package/src/forge/react/index.ts +160 -2
  333. package/src/forge/refactor/index.ts +1 -0
  334. package/src/forge/repair/rules/index.ts +2 -2
  335. package/src/forge/repository-analysis/adapters/common.ts +72 -0
  336. package/src/forge/repository-analysis/adapters/docker.ts +185 -0
  337. package/src/forge/repository-analysis/adapters/java.ts +133 -0
  338. package/src/forge/repository-analysis/adapters/typescript.ts +350 -0
  339. package/src/forge/repository-analysis/adapters/vue.ts +71 -0
  340. package/src/forge/repository-analysis/analyze.ts +434 -0
  341. package/src/forge/repository-analysis/benchmark.ts +336 -0
  342. package/src/forge/repository-analysis/cache-gc.ts +154 -0
  343. package/src/forge/repository-analysis/check-selection.ts +123 -0
  344. package/src/forge/repository-analysis/context-node.ts +17 -0
  345. package/src/forge/repository-analysis/context.ts +456 -0
  346. package/src/forge/repository-analysis/http-resolution.ts +135 -0
  347. package/src/forge/repository-analysis/identity.ts +45 -0
  348. package/src/forge/repository-analysis/index.ts +18 -0
  349. package/src/forge/repository-analysis/nuxt-resolution.ts +188 -0
  350. package/src/forge/repository-analysis/quality.ts +60 -0
  351. package/src/forge/repository-analysis/retrieval.ts +309 -0
  352. package/src/forge/repository-analysis/runtime-artifacts.ts +195 -0
  353. package/src/forge/repository-analysis/runtime-observation.ts +325 -0
  354. package/src/forge/repository-analysis/scanner.ts +174 -0
  355. package/src/forge/repository-analysis/storage.ts +193 -0
  356. package/src/forge/repository-analysis/test-associations.ts +59 -0
  357. package/src/forge/repository-analysis/types.ts +99 -0
  358. package/src/forge/repository-manifest/index.ts +178 -0
  359. package/src/forge/repository-manifest/types.ts +63 -0
  360. package/src/forge/review/index.ts +162 -12
  361. package/src/forge/review/types.ts +15 -0
  362. package/src/forge/runtime/ai/providers.ts +19 -4
  363. package/src/forge/runtime/auth/config.ts +17 -0
  364. package/src/forge/runtime/auth/evaluate.ts +15 -2
  365. package/src/forge/runtime/auth/resolve.ts +61 -6
  366. package/src/forge/runtime/auth/types.ts +1 -0
  367. package/src/forge/runtime/db/factory.ts +1 -3
  368. package/src/forge/runtime/db/generated-client.ts +13 -2
  369. package/src/forge/runtime/db/memory-adapter.ts +139 -32
  370. package/src/forge/runtime/db/pglite-adapter.ts +285 -2
  371. package/src/forge/runtime/executor.ts +129 -16
  372. package/src/forge/runtime/external/bridge.ts +663 -0
  373. package/src/forge/runtime/runner/run-entry.ts +16 -7
  374. package/src/forge/runtime/telemetry/buffer.ts +4 -1
  375. package/src/forge/runtime/telemetry/scrubber.ts +35 -5
  376. package/src/forge/runtime/webhooks/security.ts +12 -7
  377. package/src/forge/server.ts +71 -7
  378. package/src/forge/ui/index.ts +831 -19
  379. package/src/forge/ui/types.ts +3 -1
  380. package/src/forge/version.ts +1 -1
  381. package/src/forge/vue/index.ts +422 -0
  382. package/src/forge/workspace/baseline.ts +112 -0
  383. package/src/forge/workspace/change-summary.ts +292 -0
  384. package/src/forge/workspace/forge-cli.ts +42 -0
  385. package/src/forge/workspace/git-summary.ts +393 -0
  386. package/templates/agent-workroom/AGENTS.md +29 -0
  387. package/templates/agent-workroom/README.md +34 -0
  388. package/templates/agent-workroom/forge.config.ts +3 -0
  389. package/templates/agent-workroom/package.json +34 -0
  390. package/templates/agent-workroom/pnpm-workspace.yaml +2 -0
  391. package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
  392. package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
  393. package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
  394. package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
  395. package/templates/agent-workroom/src/forge/schema.ts +54 -0
  396. package/templates/agent-workroom/src/policies.ts +6 -0
  397. package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
  398. package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
  399. package/templates/agent-workroom/tsconfig.json +16 -0
  400. package/templates/agent-workroom/web/index.html +13 -0
  401. package/templates/agent-workroom/web/package.json +21 -0
  402. package/templates/agent-workroom/web/src/App.tsx +345 -0
  403. package/templates/agent-workroom/web/src/lib/forge.ts +20 -0
  404. package/templates/agent-workroom/web/src/main.tsx +13 -0
  405. package/templates/agent-workroom/web/src/styles.css +545 -0
  406. package/templates/agent-workroom/web/tsconfig.json +27 -0
  407. package/templates/agent-workroom/web/vite.config.ts +36 -0
  408. package/templates/b2b-support-web/package.json +2 -0
  409. package/templates/b2b-support-web/pnpm-workspace.yaml +2 -0
  410. package/templates/b2b-support-web/web/app/page.tsx +2 -2
  411. package/templates/b2b-support-web/web/lib/forge.ts +9 -2
  412. package/templates/b2b-support-web/web/next.config.ts +24 -0
  413. package/templates/b2b-support-web/web/package.json +1 -1
  414. package/templates/minimal-web/package.json +2 -1
  415. package/templates/minimal-web/pnpm-workspace.yaml +2 -0
  416. package/templates/minimal-web/web/index.html +1 -0
  417. package/templates/minimal-web/web/src/App.tsx +12 -6
  418. package/templates/minimal-web/web/src/lib/forge.ts +9 -2
  419. package/templates/minimal-web/web/src/styles.css +34 -0
  420. package/templates/minimal-web/web/vite.config.ts +36 -0
  421. package/templates/nuxt-web/.vscode/settings.json +14 -0
  422. package/templates/nuxt-web/.yarnrc.yml +1 -0
  423. package/templates/nuxt-web/README.md +30 -0
  424. package/templates/nuxt-web/forge.config.ts +3 -0
  425. package/templates/nuxt-web/package.json +37 -0
  426. package/templates/nuxt-web/pnpm-workspace.yaml +2 -0
  427. package/templates/nuxt-web/src/actions/logNoteCreated.ts +11 -0
  428. package/templates/nuxt-web/src/commands/createNote.ts +26 -0
  429. package/templates/nuxt-web/src/forge/schema.ts +12 -0
  430. package/templates/nuxt-web/src/policies.ts +6 -0
  431. package/templates/nuxt-web/src/queries/listNotes.ts +8 -0
  432. package/templates/nuxt-web/src/queries/liveNotes.ts +8 -0
  433. package/templates/nuxt-web/tsconfig.json +17 -0
  434. package/templates/nuxt-web/web/app.vue +67 -0
  435. package/templates/nuxt-web/web/components/LiveNotes.vue +89 -0
  436. package/templates/nuxt-web/web/components/NoteComposer.vue +100 -0
  437. package/templates/nuxt-web/web/composables/forge.ts +13 -0
  438. package/templates/nuxt-web/web/composables/useNotes.ts +24 -0
  439. package/templates/nuxt-web/web/nuxt.config.ts +11 -0
  440. package/templates/nuxt-web/web/package.json +22 -0
  441. package/templates/nuxt-web/web/plugins/forge.client.ts +10 -0
  442. package/templates/nuxt-web/web/plugins/forge.server.ts +10 -0
  443. package/templates/nuxt-web/web/server/api/forge-health.get.ts +7 -0
  444. package/templates/nuxt-web/web/tsconfig.json +6 -0
  445. package/templates/vendor-access/.vscode/settings.json +14 -0
  446. package/templates/vendor-access/README.md +30 -0
  447. package/templates/vendor-access/forge.config.ts +3 -0
  448. package/templates/vendor-access/package.json +35 -0
  449. package/templates/vendor-access/pnpm-workspace.yaml +2 -0
  450. package/templates/vendor-access/src/commands/addEvidence.ts +42 -0
  451. package/templates/vendor-access/src/commands/approveAccessRequest.ts +43 -0
  452. package/templates/vendor-access/src/commands/createAccessRequest.ts +49 -0
  453. package/templates/vendor-access/src/commands/seedVendorAccessDemo.ts +200 -0
  454. package/templates/vendor-access/src/forge/schema.ts +74 -0
  455. package/templates/vendor-access/src/policies.ts +11 -0
  456. package/templates/vendor-access/src/queries/listVendorAccessDashboard.ts +24 -0
  457. package/templates/vendor-access/src/queries/liveVendorAccessDashboard.ts +24 -0
  458. package/templates/vendor-access/tsconfig.json +17 -0
  459. package/templates/vendor-access/web/index.html +13 -0
  460. package/templates/vendor-access/web/package.json +21 -0
  461. package/templates/vendor-access/web/src/App.tsx +573 -0
  462. package/templates/vendor-access/web/src/lib/forge.ts +20 -0
  463. package/templates/vendor-access/web/src/main.tsx +205 -0
  464. package/templates/vendor-access/web/src/styles.css +682 -0
  465. package/templates/vendor-access/web/tsconfig.json +18 -0
  466. package/templates/vendor-access/web/vite.config.ts +36 -0
  467. package/src/forge/_generated/actionSubscriptions.json +0 -2
  468. package/src/forge/_generated/actionSubscriptions.ts +0 -10
  469. package/src/forge/_generated/agentAdapterManifest.json +0 -2
  470. package/src/forge/_generated/agentAdapterManifest.ts +0 -73
  471. package/src/forge/_generated/agentContract.json +0 -2
  472. package/src/forge/_generated/agentContract.ts +0 -7829
  473. package/src/forge/_generated/agentQuickstart.md +0 -34
  474. package/src/forge/_generated/agentTools.json +0 -2
  475. package/src/forge/_generated/agentTools.md +0 -16
  476. package/src/forge/_generated/agentTools.ts +0 -12
  477. package/src/forge/_generated/aiContext.ts +0 -125
  478. package/src/forge/_generated/aiModels.json +0 -2
  479. package/src/forge/_generated/aiModels.ts +0 -51
  480. package/src/forge/_generated/aiProviders.json +0 -2
  481. package/src/forge/_generated/aiProviders.ts +0 -23
  482. package/src/forge/_generated/aiRegistry.json +0 -2
  483. package/src/forge/_generated/aiRegistry.ts +0 -31
  484. package/src/forge/_generated/api.json +0 -2
  485. package/src/forge/_generated/api.ts +0 -8
  486. package/src/forge/_generated/appGraph.json +0 -2
  487. package/src/forge/_generated/appGraph.ts +0 -14937
  488. package/src/forge/_generated/appMap.md +0 -55
  489. package/src/forge/_generated/artifactManifest.json +0 -2
  490. package/src/forge/_generated/artifactManifest.ts +0 -7
  491. package/src/forge/_generated/authClaims.json +0 -2
  492. package/src/forge/_generated/authClaims.ts +0 -13
  493. package/src/forge/_generated/authConfig.json +0 -2
  494. package/src/forge/_generated/authConfig.ts +0 -17
  495. package/src/forge/_generated/authContext.ts +0 -23
  496. package/src/forge/_generated/authRegistry.json +0 -2
  497. package/src/forge/_generated/authRegistry.ts +0 -25
  498. package/src/forge/_generated/buildInfo.json +0 -2
  499. package/src/forge/_generated/buildInfo.ts +0 -9
  500. package/src/forge/_generated/capabilityMap.json +0 -2
  501. package/src/forge/_generated/capabilityMap.md +0 -15
  502. package/src/forge/_generated/capabilityMap.ts +0 -17
  503. package/src/forge/_generated/client.ts +0 -282
  504. package/src/forge/_generated/clientApi.ts +0 -9
  505. package/src/forge/_generated/clientManifest.json +0 -2
  506. package/src/forge/_generated/clientManifest.ts +0 -39
  507. package/src/forge/_generated/clientTypes.ts +0 -78
  508. package/src/forge/_generated/configRegistry.json +0 -2
  509. package/src/forge/_generated/configRegistry.ts +0 -4
  510. package/src/forge/_generated/dataGraph.json +0 -2
  511. package/src/forge/_generated/dataGraph.ts +0 -8
  512. package/src/forge/_generated/db.json +0 -2
  513. package/src/forge/_generated/db.ts +0 -2
  514. package/src/forge/_generated/dbSecurityManifest.json +0 -2
  515. package/src/forge/_generated/dbSecurityManifest.ts +0 -15
  516. package/src/forge/_generated/dbSessionContext.json +0 -2
  517. package/src/forge/_generated/dbSessionContext.ts +0 -39
  518. package/src/forge/_generated/deployManifest.json +0 -2
  519. package/src/forge/_generated/deployManifest.ts +0 -14
  520. package/src/forge/_generated/devManifest.json +0 -2
  521. package/src/forge/_generated/devManifest.ts +0 -62
  522. package/src/forge/_generated/envSchema.json +0 -2
  523. package/src/forge/_generated/envSchema.ts +0 -59
  524. package/src/forge/_generated/frontendGraph.json +0 -2
  525. package/src/forge/_generated/frontendGraph.ts +0 -27
  526. package/src/forge/_generated/importGuards.json +0 -2
  527. package/src/forge/_generated/importGuards.ts +0 -686
  528. package/src/forge/_generated/index.ts +0 -68
  529. package/src/forge/_generated/liveProductionManifest.json +0 -2
  530. package/src/forge/_generated/liveProductionManifest.ts +0 -23
  531. package/src/forge/_generated/liveProtocol.json +0 -2
  532. package/src/forge/_generated/liveProtocol.ts +0 -21
  533. package/src/forge/_generated/liveQueryRegistry.json +0 -2
  534. package/src/forge/_generated/liveQueryRegistry.ts +0 -9
  535. package/src/forge/_generated/liveTransportConfig.json +0 -2
  536. package/src/forge/_generated/liveTransportConfig.ts +0 -19
  537. package/src/forge/_generated/makeRegistry.json +0 -2
  538. package/src/forge/_generated/makeRegistry.ts +0 -177
  539. package/src/forge/_generated/makeTemplates.json +0 -2
  540. package/src/forge/_generated/makeTemplates.ts +0 -66
  541. package/src/forge/_generated/mockMap.json +0 -2
  542. package/src/forge/_generated/mockMap.ts +0 -7
  543. package/src/forge/_generated/operationPlaybooks.md +0 -167
  544. package/src/forge/_generated/packageGraph.json +0 -2
  545. package/src/forge/_generated/packageGraph.ts +0 -249334
  546. package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
  547. package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
  548. package/src/forge/_generated/permissionMatrix.json +0 -2
  549. package/src/forge/_generated/permissionMatrix.ts +0 -7
  550. package/src/forge/_generated/policyRegistry.json +0 -2
  551. package/src/forge/_generated/policyRegistry.ts +0 -11
  552. package/src/forge/_generated/queryRegistry.json +0 -2
  553. package/src/forge/_generated/queryRegistry.ts +0 -9
  554. package/src/forge/_generated/react.d.ts +0 -22
  555. package/src/forge/_generated/react.ts +0 -29
  556. package/src/forge/_generated/reactManifest.json +0 -2
  557. package/src/forge/_generated/reactManifest.ts +0 -19
  558. package/src/forge/_generated/rlsPolicies.json +0 -2
  559. package/src/forge/_generated/rlsPolicies.sql +0 -34
  560. package/src/forge/_generated/rlsPolicies.ts +0 -6
  561. package/src/forge/_generated/runtimeGraph.json +0 -2
  562. package/src/forge/_generated/runtimeGraph.ts +0 -8
  563. package/src/forge/_generated/runtimeMatrix.json +0 -2
  564. package/src/forge/_generated/runtimeMatrix.ts +0 -334130
  565. package/src/forge/_generated/runtimeRegistry.ts +0 -2
  566. package/src/forge/_generated/runtimeRules.md +0 -91
  567. package/src/forge/_generated/secretRegistry.json +0 -2
  568. package/src/forge/_generated/secretRegistry.ts +0 -50
  569. package/src/forge/_generated/secretsContext.ts +0 -11
  570. package/src/forge/_generated/serverApi.ts +0 -10
  571. package/src/forge/_generated/sourceMapManifest.json +0 -2
  572. package/src/forge/_generated/sourceMapManifest.ts +0 -7
  573. package/src/forge/_generated/sqlPlan.json +0 -2
  574. package/src/forge/_generated/sqlPlan.ts +0 -88
  575. package/src/forge/_generated/subscriptionManifest.json +0 -2
  576. package/src/forge/_generated/subscriptionManifest.ts +0 -7
  577. package/src/forge/_generated/symbolicationManifest.json +0 -2
  578. package/src/forge/_generated/symbolicationManifest.ts +0 -17
  579. package/src/forge/_generated/telemetryRegistry.json +0 -2
  580. package/src/forge/_generated/telemetryRegistry.ts +0 -9
  581. package/src/forge/_generated/telemetrySinks.json +0 -2
  582. package/src/forge/_generated/telemetrySinks.ts +0 -11
  583. package/src/forge/_generated/tenantScope.json +0 -2
  584. package/src/forge/_generated/tenantScope.ts +0 -8
  585. package/src/forge/_generated/testGraph.json +0 -2
  586. package/src/forge/_generated/testGraph.ts +0 -3452
  587. package/src/forge/_generated/testPlanRegistry.json +0 -2
  588. package/src/forge/_generated/testPlanRegistry.ts +0 -33
  589. package/src/forge/_generated/uiRoutes.json +0 -2
  590. package/src/forge/_generated/uiRoutes.ts +0 -16
  591. package/src/forge/_generated/uiScenarios.json +0 -2
  592. package/src/forge/_generated/uiScenarios.ts +0 -30
  593. package/src/forge/_generated/uiTestManifest.json +0 -2
  594. package/src/forge/_generated/uiTestManifest.ts +0 -27
  595. package/src/forge/_generated/workflowRegistry.json +0 -2
  596. package/src/forge/_generated/workflowRegistry.ts +0 -9
  597. package/src/forge/_generated/workflowSubscriptions.json +0 -2
  598. package/src/forge/_generated/workflowSubscriptions.ts +0 -10
@@ -1,8 +1,9 @@
1
- import { join } from "node:path";
1
+ import { join, relative, resolve } from "node:path";
2
2
  import { nodeFileSystem } from "../fs/index.ts";
3
3
  import type { AddOptions } from "../types/cli.ts";
4
4
  import type { Diagnostic } from "../types/diagnostic.ts";
5
5
  import type { Dependency } from "../types/package-graph.ts";
6
+ import type { IntegrationRecipe } from "../types/integration.ts";
6
7
  import type { ClassifiedPackage } from "../classifier/runtime-matrix.ts";
7
8
  import { buildAppGraph } from "../app-graph/build.ts";
8
9
  import { classify } from "../classifier/classify.ts";
@@ -16,17 +17,21 @@ import { PACKAGE_ANALYZER_VERSION } from "../package-graph/constants.ts";
16
17
  import { renderBody } from "../emitter/render.ts";
17
18
  import { hashStable } from "../primitives/hash.ts";
18
19
  import { PackageGraphCompiler } from "../package-graph/compiler.ts";
20
+ import { run as runGenerate } from "../orchestrator/run.ts";
19
21
  import {
22
+ buildAddCommand,
20
23
  detectAndCreatePackageManagerAdapter,
21
24
  dryRunRecipeFallbackMessage,
22
25
  type PackageManagerAdapter,
23
26
  } from "../package-manager/adapter.ts";
27
+ import { parsePackageName } from "../package-manager/parse-spec.ts";
24
28
  import { PackageManagerCommandError } from "../package-manager/executor.ts";
25
29
  import {
26
30
  isReferenceAlias,
27
31
  resolveByPackageName,
28
32
  resolveRecipe,
29
33
  } from "../recipes/registry.ts";
34
+ import { applyWorkOSRecipeProfile } from "../recipes/profiles.ts";
30
35
  import { discover } from "../orchestrator/discover.ts";
31
36
  import {
32
37
  loadManifest,
@@ -50,6 +55,24 @@ export interface ForgeAddOptions extends AddOptions {
50
55
 
51
56
  export interface ForgeAddResult {
52
57
  alias: string;
58
+ mode?: "integration" | "package";
59
+ targetKind?: "forge-integration" | "npm-package";
60
+ target?: string;
61
+ packageTarget?: "root" | "frontend" | "backend" | "workspace";
62
+ packageTargetReason?: string;
63
+ explanation?: string;
64
+ recipeVersion?: string;
65
+ recipePackages?: string[];
66
+ requiredSecrets?: string[];
67
+ optionalSecrets?: string[];
68
+ packageSpec?: string;
69
+ packageName?: string;
70
+ packageManager?: string;
71
+ installCommand?: string[];
72
+ nativeInstallCommand?: string[];
73
+ avoidedManualCommand?: string;
74
+ installCwd?: string;
75
+ installWorkspace?: string;
53
76
  changed: string[];
54
77
  unchanged: string[];
55
78
  warnings: Diagnostic[];
@@ -58,6 +81,337 @@ export interface ForgeAddResult {
58
81
  failureKind?: string;
59
82
  }
60
83
 
84
+ function recipeResultMetadata(recipe: IntegrationRecipe): Pick<
85
+ ForgeAddResult,
86
+ "recipeVersion" | "recipePackages" | "requiredSecrets" | "optionalSecrets"
87
+ > {
88
+ return {
89
+ recipeVersion: recipe.recipeVersion,
90
+ recipePackages: recipe.packages.map((pkg) => pkg.packageName),
91
+ requiredSecrets: recipe.secrets.filter((secret) => secret.required !== false).map((secret) => secret.envVar),
92
+ optionalSecrets: recipe.secrets.filter((secret) => secret.required === false).map((secret) => secret.envVar),
93
+ };
94
+ }
95
+
96
+ function addExplanation(result: ForgeAddResult): string {
97
+ if (result.mode === "package") {
98
+ const location = result.target && result.target !== "root" ? `${result.target}/package.json` : "package.json";
99
+ const command = result.installCommand?.join(" ") ?? `package manager add ${result.packageSpec ?? result.alias}`;
100
+ const target = result.packageTarget && result.packageTarget !== "root" ? ` (${result.packageTarget})` : "";
101
+ return `Adds npm package '${result.packageSpec ?? result.alias}' to ${location}${target}, then refreshes Forge package evidence. Forge runs the native install command for you: ${command}.`;
102
+ }
103
+
104
+ if (result.failureKind === "unknown_alias") {
105
+ return `No Forge integration recipe exists for '${result.alias}'. Use 'forge add ${result.alias}' or 'forge add package ${result.alias}' for a normal npm package, or choose a supported integration alias.`;
106
+ }
107
+
108
+ return `Applies the Forge integration recipe '${result.alias}', including package install, generated adapters, secret-name metadata, runtime guards, and Forge lock evidence.`;
109
+ }
110
+
111
+ function finalizeAddResult(result: Omit<ForgeAddResult, "targetKind" | "explanation">): ForgeAddResult {
112
+ const mode = result.mode ?? "integration";
113
+ const withMode: ForgeAddResult = {
114
+ ...result,
115
+ mode,
116
+ targetKind: mode === "package" ? "npm-package" : "forge-integration",
117
+ };
118
+ return {
119
+ ...withMode,
120
+ explanation: addExplanation(withMode),
121
+ };
122
+ }
123
+
124
+ function packageJsonRelativeFor(workspace?: string): string {
125
+ return workspace ? `${workspace.replace(/\\/g, "/")}/package.json` : "package.json";
126
+ }
127
+
128
+ function readPackageJsonDependencies(packageJsonPath: string): Record<string, string> {
129
+ if (!nodeFileSystem.exists(packageJsonPath)) {
130
+ return {};
131
+ }
132
+ try {
133
+ const parsed = JSON.parse(nodeFileSystem.readText(packageJsonPath) ?? "{}") as {
134
+ dependencies?: Record<string, string>;
135
+ devDependencies?: Record<string, string>;
136
+ peerDependencies?: Record<string, string>;
137
+ optionalDependencies?: Record<string, string>;
138
+ };
139
+ return {
140
+ ...(parsed.dependencies ?? {}),
141
+ ...(parsed.devDependencies ?? {}),
142
+ ...(parsed.peerDependencies ?? {}),
143
+ ...(parsed.optionalDependencies ?? {}),
144
+ };
145
+ } catch {
146
+ return {};
147
+ }
148
+ }
149
+
150
+ function packageJsonDeclares(packageJsonPath: string, packageName: string): boolean {
151
+ return Object.prototype.hasOwnProperty.call(readPackageJsonDependencies(packageJsonPath), packageName);
152
+ }
153
+
154
+ function packageInstallEvidence(installRoot: string, packageName: string): {
155
+ installPath: string;
156
+ version: string;
157
+ } | null {
158
+ const installPath = join(installRoot, "node_modules", ...packageName.split("/"));
159
+ const packageJsonPath = join(installPath, "package.json");
160
+ if (!nodeFileSystem.exists(packageJsonPath)) {
161
+ return null;
162
+ }
163
+ try {
164
+ const parsed = JSON.parse(nodeFileSystem.readText(packageJsonPath) ?? "{}") as {
165
+ version?: unknown;
166
+ };
167
+ return {
168
+ installPath,
169
+ version: typeof parsed.version === "string" && parsed.version.length > 0 ? parsed.version : "0.0.0",
170
+ };
171
+ } catch {
172
+ return {
173
+ installPath,
174
+ version: "0.0.0",
175
+ };
176
+ }
177
+ }
178
+
179
+ function dependencyRangeFromInstalledVersion(version: string): string {
180
+ if (/^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/.test(version)) {
181
+ return `^${version}`;
182
+ }
183
+ return version && version !== "0.0.0" ? version : "*";
184
+ }
185
+
186
+ function ensurePackageJsonDependency(packageJsonPath: string, packageName: string, version: string): boolean {
187
+ if (!nodeFileSystem.exists(packageJsonPath) || packageJsonDeclares(packageJsonPath, packageName)) {
188
+ return false;
189
+ }
190
+ const parsed = JSON.parse(nodeFileSystem.readText(packageJsonPath) ?? "{}") as {
191
+ dependencies?: Record<string, string>;
192
+ };
193
+ parsed.dependencies = {
194
+ ...(parsed.dependencies ?? {}),
195
+ [packageName]: dependencyRangeFromInstalledVersion(version),
196
+ };
197
+ parsed.dependencies = Object.fromEntries(
198
+ Object.entries(parsed.dependencies).sort(([left], [right]) => left.localeCompare(right)),
199
+ );
200
+ nodeFileSystem.writeText(packageJsonPath, `${JSON.stringify(parsed, null, 2)}\n`);
201
+ return true;
202
+ }
203
+
204
+ async function addPackageOrContinueIfInstalled(input: {
205
+ pm: PackageManagerAdapter;
206
+ packageName: string;
207
+ workspaceRoot: string;
208
+ installRoot: string;
209
+ packageJsonPath: string;
210
+ ignoreScripts: boolean;
211
+ warnings: Diagnostic[];
212
+ doctorCommand: string;
213
+ allowInstalledFallback: boolean;
214
+ }): Promise<void> {
215
+ if (packageJsonDeclares(input.packageJsonPath, input.packageName)) {
216
+ input.warnings.push(
217
+ createDiagnostic({
218
+ severity: "warning",
219
+ code: "FORGE_ADD_PACKAGE_ALREADY_DECLARED",
220
+ message: `${input.packageName} is already declared in ${relative(input.workspaceRoot, input.packageJsonPath).replace(/\\/g, "/")}; skipped package-manager install and continued recipe generation.`,
221
+ fixHint: `Run your package manager install if node_modules is missing, then rerun ${input.doctorCommand}.`,
222
+ suggestedCommands: [input.doctorCommand],
223
+ }),
224
+ );
225
+ return;
226
+ }
227
+
228
+ try {
229
+ await input.pm.add(input.packageName, {
230
+ cwd: input.installRoot,
231
+ ignoreScripts: input.ignoreScripts,
232
+ });
233
+ return;
234
+ } catch (error) {
235
+ if (!input.allowInstalledFallback) {
236
+ throw error;
237
+ }
238
+ const evidence = packageInstallEvidence(input.installRoot, input.packageName);
239
+ if (!evidence) {
240
+ throw error;
241
+ }
242
+ const declared = ensurePackageJsonDependency(input.packageJsonPath, input.packageName, evidence.version);
243
+ input.warnings.push(
244
+ createDiagnostic({
245
+ severity: "warning",
246
+ code: "FORGE_ADD_PACKAGE_ALREADY_INSTALLED",
247
+ message: `package-manager install for ${input.packageName} failed, but the package is already installed at ${relative(input.workspaceRoot, evidence.installPath).replace(/\\/g, "/")}; ${declared ? "recorded it in package.json and continued" : "continued recipe generation"}.`,
248
+ fixHint: `Rerun ${input.doctorCommand}; if node_modules was restored from cache, this is expected.`,
249
+ suggestedCommands: [input.doctorCommand],
250
+ }),
251
+ );
252
+ }
253
+ }
254
+
255
+ type PackageAddScope = "root" | "frontend" | "backend" | "workspace";
256
+
257
+ interface NormalizedPackageRequest {
258
+ spec: string;
259
+ packageTarget: PackageAddScope;
260
+ packageTargetReason: string;
261
+ installWorkspace?: string;
262
+ forcePackageMode: boolean;
263
+ }
264
+
265
+ function parseScopedPackageSpec(spec: string): {
266
+ spec: string;
267
+ target?: "frontend" | "backend";
268
+ forcePackageMode: boolean;
269
+ } {
270
+ const trimmed = spec.trim();
271
+ const match = /^(frontend|front|web|client|backend|back|server|api|root):(.+)$/i.exec(trimmed);
272
+ if (!match) {
273
+ return { spec: trimmed, forcePackageMode: false };
274
+ }
275
+ const scope = match[1]!.toLowerCase();
276
+ return {
277
+ spec: match[2]!.trim(),
278
+ target: scope === "frontend" || scope === "front" || scope === "web" || scope === "client"
279
+ ? "frontend"
280
+ : "backend",
281
+ forcePackageMode: true,
282
+ };
283
+ }
284
+
285
+ function findFrontendWorkspace(workspaceRoot: string): string | undefined {
286
+ for (const candidate of ["web", "frontend", "client", "apps/web", "packages/web"]) {
287
+ if (nodeFileSystem.exists(join(workspaceRoot, candidate, "package.json"))) {
288
+ return candidate;
289
+ }
290
+ }
291
+ return undefined;
292
+ }
293
+
294
+ function normalizePackageRequest(
295
+ spec: string,
296
+ options: ForgeAddOptions,
297
+ ): { request: NormalizedPackageRequest; error?: Diagnostic } {
298
+ const scoped = parseScopedPackageSpec(spec);
299
+ const requestedTarget = options.packageTarget ?? scoped.target;
300
+ if (options.installWorkspace?.trim()) {
301
+ return {
302
+ request: {
303
+ spec: scoped.spec,
304
+ packageTarget: "workspace",
305
+ packageTargetReason: `explicit --workspace ${options.installWorkspace.trim()}`,
306
+ installWorkspace: options.installWorkspace.trim(),
307
+ forcePackageMode: scoped.forcePackageMode,
308
+ },
309
+ };
310
+ }
311
+ if (requestedTarget === "frontend") {
312
+ const workspace = findFrontendWorkspace(options.workspaceRoot);
313
+ if (!workspace) {
314
+ return {
315
+ request: {
316
+ spec: scoped.spec,
317
+ packageTarget: "frontend",
318
+ packageTargetReason: "frontend target requested, but no frontend package.json was detected",
319
+ forcePackageMode: true,
320
+ },
321
+ error: createDiagnostic({
322
+ severity: "error",
323
+ code: "FORGE_ADD_FRONTEND_WORKSPACE_MISSING",
324
+ message: "frontend package target requested, but no frontend package.json was found under web, frontend, client, apps/web, or packages/web",
325
+ fixHint: "Create a frontend with forge make ui, pass --workspace <path>, or install as a backend/root package.",
326
+ suggestedCommands: [
327
+ "forge make ui --framework vite --dry-run --json",
328
+ `forge add ${scoped.spec} --workspace web --dry-run --json`,
329
+ `forge add backend:${scoped.spec} --dry-run --json`,
330
+ ],
331
+ }),
332
+ };
333
+ }
334
+ return {
335
+ request: {
336
+ spec: scoped.spec,
337
+ packageTarget: "frontend",
338
+ packageTargetReason: `frontend target resolved to ${workspace}/package.json`,
339
+ installWorkspace: workspace,
340
+ forcePackageMode: true,
341
+ },
342
+ };
343
+ }
344
+ if (requestedTarget === "backend") {
345
+ return {
346
+ request: {
347
+ spec: scoped.spec,
348
+ packageTarget: "backend",
349
+ packageTargetReason: "backend target resolves to the Forge app root package.json",
350
+ forcePackageMode: true,
351
+ },
352
+ };
353
+ }
354
+ return {
355
+ request: {
356
+ spec: scoped.spec,
357
+ packageTarget: "root",
358
+ packageTargetReason: "default package target resolves to the Forge app root package.json",
359
+ forcePackageMode: scoped.forcePackageMode,
360
+ },
361
+ };
362
+ }
363
+
364
+ function resolveInstallRoot(options: ForgeAddOptions): {
365
+ installRoot: string;
366
+ target: string;
367
+ extraSnapshotPaths: string[];
368
+ error?: Diagnostic;
369
+ } {
370
+ const workspace = options.installWorkspace?.trim();
371
+ if (!workspace) {
372
+ return {
373
+ installRoot: options.workspaceRoot,
374
+ target: "root",
375
+ extraSnapshotPaths: [],
376
+ };
377
+ }
378
+
379
+ const workspaceRoot = resolve(options.workspaceRoot);
380
+ const installRoot = resolve(workspaceRoot, workspace);
381
+ const rel = relative(workspaceRoot, installRoot).replace(/\\/g, "/");
382
+ if (rel.startsWith("..") || rel === "") {
383
+ return {
384
+ installRoot,
385
+ target: workspace,
386
+ extraSnapshotPaths: [],
387
+ error: createDiagnostic({
388
+ severity: "error",
389
+ code: "FORGE_ADD_INVALID_WORKSPACE",
390
+ message: `workspace '${workspace}' must resolve inside the Forge app`,
391
+ }),
392
+ };
393
+ }
394
+
395
+ if (!nodeFileSystem.exists(join(installRoot, "package.json"))) {
396
+ return {
397
+ installRoot,
398
+ target: rel,
399
+ extraSnapshotPaths: [],
400
+ error: createDiagnostic({
401
+ severity: "error",
402
+ code: "FORGE_ADD_INVALID_WORKSPACE",
403
+ message: `workspace '${workspace}' does not contain a package.json`,
404
+ }),
405
+ };
406
+ }
407
+
408
+ return {
409
+ installRoot,
410
+ target: rel,
411
+ extraSnapshotPaths: [packageJsonRelativeFor(rel)],
412
+ };
413
+ }
414
+
61
415
  function failureKind(errors: Diagnostic[]): string | undefined {
62
416
  if (errors.length === 0) {
63
417
  return undefined;
@@ -119,8 +473,318 @@ function dependencyFromInstall(
119
473
  };
120
474
  }
121
475
 
476
+ function fileHashOrNull(path: string): string | null {
477
+ if (!nodeFileSystem.exists(path)) {
478
+ return null;
479
+ }
480
+ return hashStable(nodeFileSystem.readText(path) ?? "");
481
+ }
482
+
483
+ function snapshotPackageManagerFiles(
484
+ workspaceRoot: string,
485
+ pm: PackageManagerAdapter,
486
+ extraPaths: string[] = [],
487
+ ): Map<string, string | null> {
488
+ const paths = [...new Set(["package.json", pm.lockfile, ...extraPaths])];
489
+ return new Map(
490
+ paths.map((path) => [
491
+ path,
492
+ fileHashOrNull(join(workspaceRoot, path)),
493
+ ]),
494
+ );
495
+ }
496
+
497
+ function changedPackageManagerFiles(
498
+ workspaceRoot: string,
499
+ before: Map<string, string | null>,
500
+ ): string[] {
501
+ const changed: string[] = [];
502
+ for (const [path, previousHash] of before) {
503
+ if (fileHashOrNull(join(workspaceRoot, path)) !== previousHash) {
504
+ changed.push(path);
505
+ }
506
+ }
507
+ return changed.sort();
508
+ }
509
+
510
+ function workosFrontendWorkspace(workspaceRoot: string): string | undefined {
511
+ return findFrontendWorkspace(workspaceRoot);
512
+ }
513
+
514
+ function parseDotEnv(text: string | null): Record<string, string> {
515
+ const result: Record<string, string> = {};
516
+ for (const line of (text ?? "").split(/\r?\n/)) {
517
+ const trimmed = line.trim();
518
+ if (!trimmed || trimmed.startsWith("#")) continue;
519
+ const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
520
+ if (!match) continue;
521
+ result[match[1]!] = match[2]!.replace(/^["']|["']$/g, "");
522
+ }
523
+ return result;
524
+ }
525
+
526
+ function renderMergedDotEnv(current: string | null, values: Record<string, string>): string {
527
+ const lines = (current ?? "").split(/\r?\n/);
528
+ const seen = new Set<string>();
529
+ const next = lines.map((line) => {
530
+ const match = line.match(/^([A-Za-z_][A-Za-z0-9_]*)=/);
531
+ if (!match || !(match[1]! in values)) {
532
+ return line;
533
+ }
534
+ seen.add(match[1]!);
535
+ return `${match[1]}=${values[match[1]!]}`;
536
+ });
537
+ for (const [key, value] of Object.entries(values)) {
538
+ if (!seen.has(key)) {
539
+ next.push(`${key}=${value}`);
540
+ }
541
+ }
542
+ return `${next.filter((line, index, all) => line.length > 0 || index < all.length - 1).join("\n").trimEnd()}\n`;
543
+ }
544
+
545
+ function syncWorkOSPublicWebEnv(workspaceRoot: string, frontendWorkspace: string): { changed: string[] } {
546
+ const rootLocal = parseDotEnv(nodeFileSystem.readText(join(workspaceRoot, ".env.local")));
547
+ const rootExample = parseDotEnv(nodeFileSystem.readText(join(workspaceRoot, ".env.example")));
548
+ const source = { ...rootExample, ...rootLocal };
549
+ const clientId = source.VITE_WORKOS_CLIENT_ID || source.WORKOS_CLIENT_ID || "";
550
+ const redirectUri = source.VITE_WORKOS_REDIRECT_URI || source.WORKOS_REDIRECT_URI || "http://localhost:5173/callback";
551
+ const values: Record<string, string> = {
552
+ VITE_FORGE_URL: source.VITE_FORGE_URL || "http://localhost:3765",
553
+ VITE_WORKOS_CLIENT_ID: clientId,
554
+ VITE_WORKOS_REDIRECT_URI: redirectUri,
555
+ VITE_FORGE_WORKOS_TENANT_MAP: source.VITE_FORGE_WORKOS_TENANT_MAP || "",
556
+ VITE_FORGE_WORKOS_TENANT_ID_FALLBACK: source.VITE_FORGE_WORKOS_TENANT_ID_FALLBACK || "",
557
+ };
558
+ const envRel = `${frontendWorkspace}/.env.local`;
559
+ const envPath = join(workspaceRoot, envRel);
560
+ const current = nodeFileSystem.readText(envPath);
561
+ const next = renderMergedDotEnv(current, values);
562
+ if (current === next) {
563
+ return { changed: [] };
564
+ }
565
+ nodeFileSystem.mkdirp(join(workspaceRoot, frontendWorkspace));
566
+ nodeFileSystem.writeText(envPath, next);
567
+ return { changed: [envRel] };
568
+ }
569
+
570
+ function connectWorkOSReactRoot(workspaceRoot: string, frontendWorkspace: string): {
571
+ changed: string[];
572
+ warnings: Diagnostic[];
573
+ } {
574
+ const mainRel = `${frontendWorkspace}/src/main.tsx`;
575
+ const mainPath = join(workspaceRoot, mainRel);
576
+ const current = nodeFileSystem.readText(mainPath);
577
+ if (current === null) {
578
+ return {
579
+ changed: [],
580
+ warnings: [
581
+ createDiagnostic({
582
+ severity: "warning",
583
+ code: "FORGE_WORKOS_AUTHKIT_ROOT_NOT_FOUND",
584
+ message: `generated WorkOS AuthKit bridge, but ${mainRel} was not found`,
585
+ fixHint: "Wrap the web app root with ForgeWorkOSAuthProvider from ./lib/workos-auth.",
586
+ suggestedCommands: ["forge workos doctor --json", "forge inspect ui --json"],
587
+ }),
588
+ ],
589
+ };
590
+ }
591
+ if (current.includes("ForgeWorkOSAuthProvider")) {
592
+ return { changed: [], warnings: [] };
593
+ }
594
+
595
+ const canRewriteVendorAccessRoot =
596
+ current.includes('import { ForgeProvider, forgeUrl } from "./lib/forge";') &&
597
+ current.includes("function LocalForgeProvider") &&
598
+ (
599
+ current.includes("<LocalForgeProvider persona={persona}>{app}</LocalForgeProvider>") ||
600
+ current.includes("<LocalForgeProvider persona={signedInPersona}>")
601
+ );
602
+
603
+ if (canRewriteVendorAccessRoot) {
604
+ const personaType = current.includes("LocalPersona") ? "LocalPersona" : "DemoPersona";
605
+ const withWorkOSImport = current.replace(
606
+ 'import { ForgeProvider, forgeUrl } from "./lib/forge";',
607
+ [
608
+ 'import { ForgeProvider, forgeUrl } from "./lib/forge";',
609
+ 'import { ForgeWorkOSAuthProvider, hasWorkOSBrowserConfig, useForgeWorkOSSession, useWorkOSAuth } from "./lib/workos-auth";',
610
+ ].join("\n"),
611
+ );
612
+ if (withWorkOSImport.includes("function LoginScreen(") && !withWorkOSImport.includes("function WorkOSVendorAccessRoot()")) {
613
+ const withWorkOSEntry = withWorkOSImport.replace(
614
+ "function Root() {\n",
615
+ [
616
+ "function Root() {",
617
+ " if (hasWorkOSBrowserConfig()) {",
618
+ " return (",
619
+ " <ForgeWorkOSAuthProvider>",
620
+ " <WorkOSVendorAccessRoot />",
621
+ " </ForgeWorkOSAuthProvider>",
622
+ " );",
623
+ " }",
624
+ "",
625
+ ].join("\n"),
626
+ );
627
+ const workOSRoot = [
628
+ "function WorkOSVendorAccessRoot() {",
629
+ " const auth = useWorkOSAuth();",
630
+ " const workosSession = useForgeWorkOSSession();",
631
+ "",
632
+ " if (auth.isLoading || (auth.user && workosSession.loading)) {",
633
+ " return (",
634
+ " <main className=\"login-shell\">",
635
+ " <section className=\"login-panel\">",
636
+ " <p className=\"eyebrow\">Secure workspace</p>",
637
+ " <h1>Checking your session</h1>",
638
+ " </section>",
639
+ " </main>",
640
+ " );",
641
+ " }",
642
+ "",
643
+ " if (auth.user && workosSession.error) {",
644
+ " return (",
645
+ " <main className=\"login-shell\">",
646
+ " <section className=\"login-panel\">",
647
+ " <p className=\"eyebrow\">Identity provider</p>",
648
+ " <h1>Session unavailable</h1>",
649
+ " <p className=\"notice error\">{workosSession.error.message}</p>",
650
+ " <div className=\"login-form\">",
651
+ " <button type=\"button\" onClick={() => void workosSession.refresh()}>Retry session</button>",
652
+ " <button className=\"secondary\" type=\"button\" onClick={() => auth.signOut()}>Sign out</button>",
653
+ " </div>",
654
+ " </section>",
655
+ " </main>",
656
+ " );",
657
+ " }",
658
+ "",
659
+ " if (!auth.user) {",
660
+ " return (",
661
+ " <main className=\"login-shell\">",
662
+ " <section className=\"login-panel\">",
663
+ " <div className=\"brand login-brand\">",
664
+ " <span className=\"brand-mark\">VA</span>",
665
+ " <div>",
666
+ " <strong>Vendor Access</strong>",
667
+ " <span>Risk operations</span>",
668
+ " </div>",
669
+ " </div>",
670
+ " <p className=\"eyebrow\">WorkOS AuthKit</p>",
671
+ " <h1>Sign in to review vendor access</h1>",
672
+ " <div className=\"login-form\">",
673
+ " <button type=\"button\" onClick={() => auth.signIn()}>Sign in with WorkOS</button>",
674
+ " <button className=\"secondary\" type=\"button\" onClick={() => auth.signUp()}>Create account</button>",
675
+ " </div>",
676
+ " </section>",
677
+ " <aside className=\"login-context\">",
678
+ " <span>Identity provider</span>",
679
+ " <strong>AuthKit session required</strong>",
680
+ " <p>Organization, role, and permissions come from your signed-in workspace session.</p>",
681
+ " </aside>",
682
+ " </main>",
683
+ " );",
684
+ " }",
685
+ "",
686
+ " const claims = workosSession.session?.claims;",
687
+ " const email = workosSession.session?.user?.email ?? auth.user.email ?? claims?.email ?? \"workos-user@example.com\";",
688
+ " const organizationId = claims?.organization_id ?? \"workos-organization\";",
689
+ " const role = claims?.role ?? claims?.roles?.[0] ?? \"member\";",
690
+ " const permissions = claims?.permissions ?? [];",
691
+ ` const persona: ${personaType} = {`,
692
+ " id: `workos:${organizationId}:${email}`,",
693
+ " label: email,",
694
+ " email,",
695
+ " organizationId,",
696
+ " organizationName: organizationId,",
697
+ " role,",
698
+ " permissions,",
699
+ " };",
700
+ "",
701
+ " return (",
702
+ " <App",
703
+ " persona={persona}",
704
+ " personas={[persona]}",
705
+ " onPersonaChange={() => undefined}",
706
+ " onSignOut={() => auth.signOut()}",
707
+ " />",
708
+ " );",
709
+ "}",
710
+ "",
711
+ ].join("\n");
712
+ const next = withWorkOSEntry.replace("function LoginScreen(", `${workOSRoot}function LoginScreen(`);
713
+ if (next !== current) {
714
+ nodeFileSystem.writeText(mainPath, next);
715
+ return { changed: [mainRel], warnings: [] };
716
+ }
717
+ }
718
+ let next = withWorkOSImport.replace(
719
+ "<LocalForgeProvider persona={persona}>{app}</LocalForgeProvider>",
720
+ [
721
+ "hasWorkOSBrowserConfig() ? (",
722
+ " <ForgeWorkOSAuthProvider>{app}</ForgeWorkOSAuthProvider>",
723
+ " ) : (",
724
+ " <LocalForgeProvider persona={persona}>{app}</LocalForgeProvider>",
725
+ " )",
726
+ ].join("\n "),
727
+ );
728
+ if (next === withWorkOSImport) {
729
+ const rootReturn = / return \(\n <LocalForgeProvider persona=\{signedInPersona\}>\n([\s\S]*?) <\/LocalForgeProvider>\n \);/m;
730
+ next = withWorkOSImport.replace(rootReturn, (_match, appBody: string) => {
731
+ const app = String(appBody).replace(/^ /gm, " ");
732
+ return [
733
+ " const app = (",
734
+ app.trimEnd(),
735
+ " );",
736
+ "",
737
+ " return hasWorkOSBrowserConfig() ? (",
738
+ " <ForgeWorkOSAuthProvider>{app}</ForgeWorkOSAuthProvider>",
739
+ " ) : (",
740
+ " <LocalForgeProvider persona={signedInPersona}>{app}</LocalForgeProvider>",
741
+ " );",
742
+ ].join("\n");
743
+ });
744
+ }
745
+ if (next !== current) {
746
+ nodeFileSystem.writeText(mainPath, next);
747
+ return { changed: [mainRel], warnings: [] };
748
+ }
749
+ }
750
+
751
+ const canRewriteDefaultViteRoot =
752
+ current.includes('import { ForgeProvider, forgeUrl } from "./lib/forge";') &&
753
+ current.includes("<ForgeProvider url={forgeUrl} devAuth>") &&
754
+ current.includes("</ForgeProvider>");
755
+
756
+ if (!canRewriteDefaultViteRoot) {
757
+ return {
758
+ changed: [],
759
+ warnings: [
760
+ createDiagnostic({
761
+ severity: "warning",
762
+ code: "FORGE_WORKOS_AUTHKIT_ROOT_CUSTOM",
763
+ message: `generated WorkOS AuthKit bridge, but ${mainRel} is custom and was not rewritten automatically`,
764
+ fixHint: "Import ForgeWorkOSAuthProvider from ./lib/workos-auth and wrap the app root with it so AuthKit tokens are passed to ForgeProvider.",
765
+ suggestedCommands: ["forge workos doctor --json", "forge inspect ui --json"],
766
+ }),
767
+ ],
768
+ };
769
+ }
770
+
771
+ const next = current
772
+ .replace(
773
+ 'import { ForgeProvider, forgeUrl } from "./lib/forge";',
774
+ 'import { ForgeWorkOSAuthProvider } from "./lib/workos-auth";',
775
+ )
776
+ .replace("<ForgeProvider url={forgeUrl} devAuth>", "<ForgeWorkOSAuthProvider>")
777
+ .replace("</ForgeProvider>", "</ForgeWorkOSAuthProvider>");
778
+
779
+ if (next !== current) {
780
+ nodeFileSystem.writeText(mainPath, next);
781
+ return { changed: [mainRel], warnings: [] };
782
+ }
783
+ return { changed: [], warnings: [] };
784
+ }
785
+
122
786
  async function analyzeRecipePackages(
123
- recipe: NonNullable<ReturnType<typeof resolveRecipe>>,
787
+ recipe: IntegrationRecipe,
124
788
  ctx: ReturnType<typeof discover>,
125
789
  installRoot: string,
126
790
  options: ForgeAddOptions,
@@ -177,7 +841,7 @@ async function analyzeRecipePackages(
177
841
 
178
842
  async function buildAddPlan(
179
843
  alias: string,
180
- recipe: NonNullable<ReturnType<typeof resolveRecipe>>,
844
+ recipe: IntegrationRecipe,
181
845
  ctx: ReturnType<typeof discover>,
182
846
  installRoot: string,
183
847
  options: ForgeAddOptions,
@@ -258,37 +922,100 @@ export async function forgeAdd(
258
922
  alias: string,
259
923
  options: ForgeAddOptions,
260
924
  ): Promise<ForgeAddResult> {
261
- const normalized = alias.trim().toLowerCase();
925
+ const packageRequestResult = normalizePackageRequest(alias, options);
926
+ const packageRequest = packageRequestResult.request;
927
+ const effectiveOptions: ForgeAddOptions = {
928
+ ...options,
929
+ installWorkspace: packageRequest.installWorkspace,
930
+ };
931
+ const packageModeRequested =
932
+ options.mode === "package" ||
933
+ packageRequest.forcePackageMode ||
934
+ options.packageTarget !== undefined;
935
+ if (packageRequestResult.error && packageModeRequested) {
936
+ return finalizeAddResult({
937
+ alias: packageRequest.spec,
938
+ mode: "package",
939
+ target: packageRequest.installWorkspace ?? packageRequest.packageTarget,
940
+ packageTarget: packageRequest.packageTarget,
941
+ packageTargetReason: packageRequest.packageTargetReason,
942
+ packageSpec: packageRequest.spec,
943
+ packageName: parsePackageName(packageRequest.spec),
944
+ changed: [],
945
+ unchanged: [],
946
+ warnings: [],
947
+ errors: [packageRequestResult.error],
948
+ exitCode: 1,
949
+ failureKind: "invalid_workspace",
950
+ });
951
+ }
952
+ if (options.mode === "integration" && (packageRequest.forcePackageMode || options.packageTarget)) {
953
+ const error = createDiagnostic({
954
+ severity: "error",
955
+ code: "FORGE_ADD_SCOPED_INTEGRATION",
956
+ message: "frontend/backend package targets only apply to normal npm package installs, not explicit integration recipes",
957
+ fixHint: "Use forge add package <spec> --frontend, forge add frontend:<spec>, or remove the frontend/backend target for an integration recipe.",
958
+ suggestedCommands: [
959
+ `forge add package ${packageRequest.spec} --frontend --dry-run --json`,
960
+ `forge add integration ${packageRequest.spec} --dry-run --json`,
961
+ ],
962
+ });
963
+ return finalizeAddResult({
964
+ alias: packageRequest.spec,
965
+ mode: "integration",
966
+ changed: [],
967
+ unchanged: [],
968
+ warnings: [],
969
+ errors: [error],
970
+ exitCode: 1,
971
+ failureKind: "invalid_target",
972
+ });
973
+ }
974
+
975
+ const normalized = packageRequest.spec.trim().toLowerCase();
262
976
  const recipe = resolveRecipe(normalized);
977
+ const mode = options.mode ?? "auto";
978
+
979
+ if (packageModeRequested) {
980
+ return forgeAddPackage(packageRequest.spec, effectiveOptions, packageRequest);
981
+ }
982
+
983
+ if (mode === "auto" && (!isReferenceAlias(normalized) || recipe === null)) {
984
+ return forgeAddPackage(packageRequest.spec, effectiveOptions, packageRequest);
985
+ }
263
986
 
264
987
  if (!isReferenceAlias(normalized) || recipe === null) {
265
988
  const error = createDiagnostic({
266
989
  severity: "error",
267
990
  code: "FORGE_UNKNOWN_ALIAS",
268
- message: `unknown integration alias '${alias}'; supported: stripe, posthog, sentry, zod, ai`,
991
+ message: `unknown integration alias '${alias}'; supported: stripe, posthog, sentry, zod, convex, workos, ai. For npm packages, use 'forge add package ${alias}' or 'forge add ${alias}'.`,
992
+ suggestedCommands: [`forge add package ${alias} --dry-run --json`, "forge add --help"],
269
993
  });
270
- return {
994
+ return finalizeAddResult({
271
995
  alias: normalized,
996
+ mode: "integration",
272
997
  changed: [],
273
998
  unchanged: [],
274
999
  warnings: [],
275
1000
  errors: [error],
276
1001
  exitCode: 1,
277
1002
  failureKind: "unknown_alias",
278
- };
1003
+ });
279
1004
  }
280
1005
 
1006
+ const effectiveRecipe = applyWorkOSRecipeProfile(recipe, effectiveOptions);
1007
+
281
1008
  const pm =
282
1009
  options.pmAdapter ??
283
1010
  detectAndCreatePackageManagerAdapter(options.workspaceRoot);
284
1011
 
285
1012
  if (options.dryRun) {
286
- const ctx = discover({ workspaceRoot: options.workspaceRoot });
1013
+ const ctx = discover({ workspaceRoot: effectiveOptions.workspaceRoot });
287
1014
  let installRoot = options.workspaceRoot;
288
1015
 
289
1016
  try {
290
1017
  const dryRun = await pm.dryRunAddWithPath(
291
- recipe.packages.map((pkg) => pkg.packageName).join(" "),
1018
+ effectiveRecipe.packages.map((pkg) => pkg.packageName).join(" "),
292
1019
  {
293
1020
  cwd: options.workspaceRoot,
294
1021
  ignoreScripts: !options.allowScripts,
@@ -299,10 +1026,10 @@ export async function forgeAdd(
299
1026
  const fallback = dryRunRecipeFallbackMessage(normalized);
300
1027
  const { emitPlan, warnings, errors } = await buildAddPlan(
301
1028
  normalized,
302
- recipe,
1029
+ effectiveRecipe,
303
1030
  ctx,
304
1031
  installRoot,
305
- options,
1032
+ effectiveOptions,
306
1033
  );
307
1034
  warnings.push(
308
1035
  createDiagnostic({
@@ -312,50 +1039,83 @@ export async function forgeAdd(
312
1039
  }),
313
1040
  );
314
1041
 
315
- return {
1042
+ return finalizeAddResult({
316
1043
  alias: normalized,
1044
+ mode: "integration",
1045
+ ...recipeResultMetadata(effectiveRecipe),
317
1046
  changed: [...emitPlan.files.map((file) => file.path), "forge.lock"],
318
1047
  unchanged: [],
319
1048
  warnings,
320
1049
  errors,
321
1050
  exitCode: errors.length > 0 ? 1 : 0,
322
1051
  failureKind: failureKind(errors),
323
- };
1052
+ });
324
1053
  }
325
1054
 
326
1055
  const { emitPlan, warnings, errors } = await buildAddPlan(
327
1056
  normalized,
328
- recipe,
1057
+ effectiveRecipe,
329
1058
  ctx,
330
1059
  installRoot,
331
- options,
1060
+ effectiveOptions,
332
1061
  );
333
1062
 
334
- return {
1063
+ return finalizeAddResult({
335
1064
  alias: normalized,
1065
+ mode: "integration",
1066
+ ...recipeResultMetadata(effectiveRecipe),
336
1067
  changed: [...emitPlan.files.map((file) => file.path), "forge.lock"],
337
1068
  unchanged: [],
338
1069
  warnings,
339
1070
  errors,
340
1071
  exitCode: errors.length > 0 ? 1 : 0,
341
1072
  failureKind: failureKind(errors),
342
- };
1073
+ });
343
1074
  }
344
1075
 
345
1076
  const snapshot = snapshotVersionControlled(options.workspaceRoot);
346
1077
 
347
1078
  try {
348
- for (const pkg of recipe.packages) {
349
- await pm.add(pkg.packageName, {
350
- cwd: options.workspaceRoot,
1079
+ const frontendWorkspace = normalized === "workos" ? workosFrontendWorkspace(options.workspaceRoot) : undefined;
1080
+ const packageManagerBefore = snapshotPackageManagerFiles(
1081
+ options.workspaceRoot,
1082
+ pm,
1083
+ frontendWorkspace ? [packageJsonRelativeFor(frontendWorkspace)] : [],
1084
+ );
1085
+ const preinstalledWarnings: Diagnostic[] = [];
1086
+ for (const pkg of effectiveRecipe.packages) {
1087
+ const rootPackageJson = join(options.workspaceRoot, "package.json");
1088
+ await addPackageOrContinueIfInstalled({
1089
+ pm,
1090
+ packageName: pkg.packageName,
1091
+ workspaceRoot: options.workspaceRoot,
1092
+ installRoot: options.workspaceRoot,
1093
+ packageJsonPath: rootPackageJson,
351
1094
  ignoreScripts: !options.allowScripts,
1095
+ warnings: preinstalledWarnings,
1096
+ doctorCommand: "forge check --json",
1097
+ allowInstalledFallback: normalized === "workos",
1098
+ });
1099
+ }
1100
+ if (frontendWorkspace) {
1101
+ const frontendPackageJson = join(options.workspaceRoot, frontendWorkspace, "package.json");
1102
+ await addPackageOrContinueIfInstalled({
1103
+ pm,
1104
+ packageName: "@workos-inc/authkit-react",
1105
+ workspaceRoot: options.workspaceRoot,
1106
+ installRoot: join(options.workspaceRoot, frontendWorkspace),
1107
+ packageJsonPath: frontendPackageJson,
1108
+ ignoreScripts: !options.allowScripts,
1109
+ warnings: preinstalledWarnings,
1110
+ doctorCommand: "forge workos doctor --json",
1111
+ allowInstalledFallback: true,
352
1112
  });
353
1113
  }
354
1114
 
355
1115
  const ctx = discover({ workspaceRoot: options.workspaceRoot });
356
1116
  const { emitPlan, warnings, errors: analyzeErrors } = await buildAddPlan(
357
1117
  normalized,
358
- recipe,
1118
+ effectiveRecipe,
359
1119
  ctx,
360
1120
  options.workspaceRoot,
361
1121
  options,
@@ -363,15 +1123,17 @@ export async function forgeAdd(
363
1123
 
364
1124
  if (analyzeErrors.length > 0) {
365
1125
  restoreVersionControlledSnapshot(options.workspaceRoot, snapshot);
366
- return {
1126
+ return finalizeAddResult({
367
1127
  alias: normalized,
1128
+ mode: "integration",
1129
+ ...recipeResultMetadata(effectiveRecipe),
368
1130
  changed: [],
369
1131
  unchanged: [],
370
1132
  warnings,
371
1133
  errors: analyzeErrors,
372
1134
  exitCode: 1,
373
1135
  failureKind: failureKind(analyzeErrors),
374
- };
1136
+ });
375
1137
  }
376
1138
 
377
1139
  const emitResult = await emit(emitPlan, {
@@ -379,20 +1141,30 @@ export async function forgeAdd(
379
1141
  mode: "write",
380
1142
  });
381
1143
 
382
- const warningsCombined = [...warnings, ...emitResult.warnings];
1144
+ const workosWeb = normalized === "workos" && frontendWorkspace
1145
+ ? (() => {
1146
+ const root = connectWorkOSReactRoot(options.workspaceRoot, frontendWorkspace);
1147
+ const env = syncWorkOSPublicWebEnv(options.workspaceRoot, frontendWorkspace);
1148
+ return { changed: [...root.changed, ...env.changed], warnings: root.warnings };
1149
+ })()
1150
+ : { changed: [] as string[], warnings: [] as Diagnostic[] };
1151
+
1152
+ const warningsCombined = [...preinstalledWarnings, ...warnings, ...emitResult.warnings, ...workosWeb.warnings];
383
1153
  const errors = [...analyzeErrors, ...emitResult.errors];
384
1154
 
385
1155
  if (errors.length > 0) {
386
1156
  restoreVersionControlledSnapshot(options.workspaceRoot, snapshot);
387
- return {
1157
+ return finalizeAddResult({
388
1158
  alias: normalized,
1159
+ mode: "integration",
1160
+ ...recipeResultMetadata(effectiveRecipe),
389
1161
  changed: [],
390
1162
  unchanged: [],
391
1163
  warnings: warningsCombined,
392
1164
  errors,
393
1165
  exitCode: 1,
394
1166
  failureKind: failureKind(errors),
395
- };
1167
+ });
396
1168
  }
397
1169
 
398
1170
  const integrityErrors = verifyLockIntegrity(
@@ -401,15 +1173,17 @@ export async function forgeAdd(
401
1173
  );
402
1174
  if (integrityErrors.length > 0) {
403
1175
  restoreVersionControlledSnapshot(options.workspaceRoot, snapshot);
404
- return {
1176
+ return finalizeAddResult({
405
1177
  alias: normalized,
1178
+ mode: "integration",
1179
+ ...recipeResultMetadata(effectiveRecipe),
406
1180
  changed: [],
407
1181
  unchanged: [],
408
1182
  warnings: warningsCombined,
409
1183
  errors: integrityErrors,
410
1184
  exitCode: 1,
411
1185
  failureKind: "lock_integrity",
412
- };
1186
+ });
413
1187
  }
414
1188
 
415
1189
  const manifest = loadManifest(ctx.cacheDir);
@@ -435,14 +1209,20 @@ export async function forgeAdd(
435
1209
  ),
436
1210
  );
437
1211
 
438
- return {
1212
+ return finalizeAddResult({
439
1213
  alias: normalized,
440
- changed: emitResult.changed,
1214
+ mode: "integration",
1215
+ ...recipeResultMetadata(effectiveRecipe),
1216
+ changed: [
1217
+ ...changedPackageManagerFiles(options.workspaceRoot, packageManagerBefore),
1218
+ ...emitResult.changed,
1219
+ ...workosWeb.changed,
1220
+ ],
441
1221
  unchanged: emitResult.unchanged,
442
1222
  warnings: warningsCombined,
443
1223
  errors: [],
444
1224
  exitCode: 0,
445
- };
1225
+ });
446
1226
  } catch (error) {
447
1227
  restoreVersionControlledSnapshot(options.workspaceRoot, snapshot);
448
1228
 
@@ -459,15 +1239,193 @@ export async function forgeAdd(
459
1239
  message: `forge add failed: ${message}`,
460
1240
  });
461
1241
 
462
- return {
1242
+ return finalizeAddResult({
463
1243
  alias: normalized,
1244
+ mode: "integration",
1245
+ ...recipeResultMetadata(effectiveRecipe),
464
1246
  changed: [],
465
1247
  unchanged: [],
466
1248
  warnings: [],
467
1249
  errors: [diagnostic],
468
1250
  exitCode: 1,
469
1251
  failureKind: "install_failed",
470
- };
1252
+ });
1253
+ }
1254
+ }
1255
+
1256
+ async function forgeAddPackage(
1257
+ spec: string,
1258
+ options: ForgeAddOptions,
1259
+ request = normalizePackageRequest(spec, options).request,
1260
+ ): Promise<ForgeAddResult> {
1261
+ const normalized = request.spec.trim();
1262
+ const packageName = parsePackageName(normalized);
1263
+ const target = resolveInstallRoot(options);
1264
+ if (target.error) {
1265
+ return finalizeAddResult({
1266
+ alias: normalized,
1267
+ mode: "package",
1268
+ target: target.target,
1269
+ packageTarget: request.packageTarget,
1270
+ packageTargetReason: request.packageTargetReason,
1271
+ changed: [],
1272
+ unchanged: [],
1273
+ warnings: [],
1274
+ errors: [target.error],
1275
+ exitCode: 1,
1276
+ failureKind: "invalid_workspace",
1277
+ });
1278
+ }
1279
+
1280
+ const pm =
1281
+ options.pmAdapter ??
1282
+ detectAndCreatePackageManagerAdapter(options.workspaceRoot);
1283
+ const packageManagerCwd = target.installRoot;
1284
+ const semanticInstallWorkspace = target.target === "root" ? undefined : target.target;
1285
+ const installCommand = buildAddCommand(pm.name, normalized, {
1286
+ ignoreScripts: !options.allowScripts,
1287
+ workspace: undefined,
1288
+ });
1289
+ const avoidedManualCommand = installCommand.join(" ");
1290
+ const installPlan = {
1291
+ packageSpec: normalized,
1292
+ packageName,
1293
+ packageManager: pm.name,
1294
+ installCommand,
1295
+ nativeInstallCommand: installCommand,
1296
+ avoidedManualCommand,
1297
+ installCwd: packageManagerCwd.replace(/\\/g, "/"),
1298
+ packageTarget: request.packageTarget,
1299
+ packageTargetReason: request.packageTargetReason,
1300
+ ...(semanticInstallWorkspace ? { installWorkspace: semanticInstallWorkspace } : {}),
1301
+ };
1302
+
1303
+ if (options.dryRun) {
1304
+ try {
1305
+ await pm.dryRunAdd(normalized, {
1306
+ cwd: packageManagerCwd,
1307
+ ignoreScripts: !options.allowScripts,
1308
+ workspace: undefined,
1309
+ });
1310
+ } catch (error) {
1311
+ const message =
1312
+ error instanceof PackageManagerCommandError
1313
+ ? error.message
1314
+ : error instanceof Error
1315
+ ? error.message
1316
+ : String(error);
1317
+ const diagnostic = createDiagnostic({
1318
+ severity: "error",
1319
+ code: "FORGE_ADD_INSTALL_FAILED",
1320
+ message: `forge add package dry-run failed: ${message}`,
1321
+ });
1322
+ return finalizeAddResult({
1323
+ alias: normalized,
1324
+ mode: "package",
1325
+ target: target.target,
1326
+ ...installPlan,
1327
+ changed: [],
1328
+ unchanged: [],
1329
+ warnings: [],
1330
+ errors: [diagnostic],
1331
+ exitCode: 1,
1332
+ failureKind: "install_failed",
1333
+ });
1334
+ }
1335
+
1336
+ return finalizeAddResult({
1337
+ alias: normalized,
1338
+ mode: "package",
1339
+ target: target.target,
1340
+ ...installPlan,
1341
+ changed: [
1342
+ packageJsonRelativeFor(target.target === "root" ? undefined : target.target),
1343
+ ],
1344
+ unchanged: [],
1345
+ warnings: [],
1346
+ errors: [],
1347
+ exitCode: 0,
1348
+ });
1349
+ }
1350
+
1351
+ const snapshot = snapshotVersionControlled(
1352
+ options.workspaceRoot,
1353
+ target.extraSnapshotPaths,
1354
+ );
1355
+
1356
+ try {
1357
+ await pm.add(normalized, {
1358
+ cwd: packageManagerCwd,
1359
+ ignoreScripts: !options.allowScripts,
1360
+ workspace: undefined,
1361
+ });
1362
+
1363
+ const generated = await runGenerate({
1364
+ workspaceRoot: options.workspaceRoot,
1365
+ check: false,
1366
+ dryRun: false,
1367
+ json: options.json,
1368
+ concurrency: 4,
1369
+ });
1370
+
1371
+ if (generated.exitCode !== 0) {
1372
+ restoreVersionControlledSnapshot(options.workspaceRoot, snapshot);
1373
+ return finalizeAddResult({
1374
+ alias: normalized,
1375
+ mode: "package",
1376
+ target: target.target,
1377
+ ...installPlan,
1378
+ changed: [],
1379
+ unchanged: [],
1380
+ warnings: generated.warnings,
1381
+ errors: generated.errors,
1382
+ exitCode: 1,
1383
+ failureKind: generated.failureKind,
1384
+ });
1385
+ }
1386
+
1387
+ return finalizeAddResult({
1388
+ alias: normalized,
1389
+ mode: "package",
1390
+ target: target.target,
1391
+ ...installPlan,
1392
+ changed: [
1393
+ packageJsonRelativeFor(target.target === "root" ? undefined : target.target),
1394
+ ...generated.changed,
1395
+ ],
1396
+ unchanged: generated.unchanged,
1397
+ warnings: generated.warnings,
1398
+ errors: generated.errors,
1399
+ exitCode: 0,
1400
+ });
1401
+ } catch (error) {
1402
+ restoreVersionControlledSnapshot(options.workspaceRoot, snapshot);
1403
+
1404
+ const message =
1405
+ error instanceof PackageManagerCommandError
1406
+ ? error.message
1407
+ : error instanceof Error
1408
+ ? error.message
1409
+ : String(error);
1410
+
1411
+ const diagnostic = createDiagnostic({
1412
+ severity: "error",
1413
+ code: "FORGE_ADD_INSTALL_FAILED",
1414
+ message: `forge add package failed: ${message}`,
1415
+ });
1416
+
1417
+ return finalizeAddResult({
1418
+ alias: normalized,
1419
+ mode: "package",
1420
+ target: target.target,
1421
+ ...installPlan,
1422
+ changed: [],
1423
+ unchanged: [],
1424
+ warnings: [],
1425
+ errors: [diagnostic],
1426
+ exitCode: 1,
1427
+ failureKind: "install_failed",
1428
+ });
471
1429
  }
472
1430
  }
473
1431