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
@@ -0,0 +1,51 @@
1
+ export interface CommandHeartbeat {
2
+ setPhase: (phase: string) => void;
3
+ stop: () => void;
4
+ }
5
+
6
+ export function startCommandHeartbeat(input: {
7
+ label: string;
8
+ enabled?: boolean;
9
+ initialPhase?: string;
10
+ intervalMs?: number;
11
+ stallMs?: number;
12
+ }): CommandHeartbeat {
13
+ if (input.enabled === false || process.env.FORGE_PROGRESS === "0") {
14
+ return {
15
+ setPhase: () => {},
16
+ stop: () => {},
17
+ };
18
+ }
19
+
20
+ const startedAt = Date.now();
21
+ const intervalMs = input.intervalMs ?? 10_000;
22
+ const stallMs = input.stallMs ?? 30_000;
23
+ let phase = input.initialPhase ?? "running";
24
+ let ticks = 0;
25
+
26
+ const timer = setInterval(() => {
27
+ ticks += 1;
28
+ const elapsedMs = Date.now() - startedAt;
29
+ const elapsedSeconds = Math.round(elapsedMs / 1000);
30
+ const stalled = elapsedMs >= stallMs;
31
+ const prefix = stalled ? "still waiting" : "working";
32
+ process.stderr.write(
33
+ `[forge] ${input.label}: ${prefix}; phase=${phase}; elapsed=${elapsedSeconds}s\n`,
34
+ );
35
+ if (stalled && ticks === Math.ceil(stallMs / intervalMs)) {
36
+ process.stderr.write(
37
+ `[forge] ${input.label}: run with --json for structured status; for dev startup failures, use forge last --json.\n`,
38
+ );
39
+ }
40
+ }, intervalMs);
41
+ timer.unref?.();
42
+
43
+ return {
44
+ setPhase: (nextPhase: string) => {
45
+ phase = nextPhase;
46
+ },
47
+ stop: () => {
48
+ clearInterval(timer);
49
+ },
50
+ };
51
+ }
@@ -1,6 +1,10 @@
1
1
  import { listQueries, runQuery, type ListQueriesResult, type RunQueryResult } from "../runtime/query/run-query.ts";
2
2
  import { resolveAuthFromCli } from "../runtime/auth/resolve.ts";
3
3
  import { createDiagnostic } from "../compiler/diagnostics/create.ts";
4
+ import {
5
+ resolveExternalQualifiedName,
6
+ runExternalEntry,
7
+ } from "../runtime/external/bridge.ts";
4
8
 
5
9
  export type QuerySubcommand = "list" | "run";
6
10
 
@@ -52,6 +56,34 @@ export async function runQueryCommand(
52
56
  role: options.role,
53
57
  });
54
58
 
59
+ const external = resolveExternalQualifiedName(options.workspaceRoot, options.name, "query");
60
+ if (external) {
61
+ const run = await runExternalEntry(options.workspaceRoot, {
62
+ kind: "query",
63
+ serviceName: external.serviceName,
64
+ entryName: external.entryName,
65
+ args: options.args,
66
+ auth,
67
+ });
68
+ return {
69
+ run: {
70
+ ok: run.ok,
71
+ result: run.result,
72
+ query: {
73
+ name: `${external.serviceName}.${external.entryName}`,
74
+ qualifiedName: `${external.serviceName}.${external.entryName}`,
75
+ file: `external:${external.serviceName}`,
76
+ symbolId: `external:${external.serviceName}:query:${external.entryName}`,
77
+ moduleId: `external:${external.serviceName}`,
78
+ },
79
+ diagnostics: run.diagnostics,
80
+ exitCode: run.exitCode,
81
+ traceId: run.traceId,
82
+ },
83
+ exitCode: run.exitCode,
84
+ };
85
+ }
86
+
55
87
  const run = await runQuery(options.workspaceRoot, options.name, {
56
88
  args: options.args,
57
89
  auth,
@@ -18,12 +18,14 @@ import {
18
18
  providerConfigDiagnostics,
19
19
  } from "../runtime/release/runtime.ts";
20
20
  import { symbolicateStacktrace } from "../runtime/release/symbolicate.ts";
21
+ import { releasePrepareNextActions, releaseReadyNextActions } from "./next-actions.ts";
21
22
 
22
23
  export type ReleaseArea = "release" | "artifacts" | "sourcemaps";
23
24
  export type ReleaseAction =
24
25
  | "prepare"
25
26
  | "inspect"
26
27
  | "check"
28
+ | "doctor"
27
29
  | "finalize"
28
30
  | "collect"
29
31
  | "list"
@@ -44,12 +46,15 @@ export interface ReleaseCommandOptions {
44
46
  target?: ReleaseExportProvider;
45
47
  allowDirty: boolean;
46
48
  allowPublicSourcemaps: boolean;
49
+ allowMissingLocalRelease?: boolean;
47
50
  }
48
51
 
49
52
  export interface ReleaseCommandResult {
50
53
  ok: boolean;
51
54
  data?: unknown;
52
55
  diagnostics: Diagnostic[];
56
+ nextActions?: string[];
57
+ failureKind?: string;
53
58
  exitCode: 0 | 1;
54
59
  }
55
60
 
@@ -125,19 +130,34 @@ export async function runReleaseCommand(
125
130
  const dir = latestReleaseDir(options.workspaceRoot);
126
131
  const diagnostics: Diagnostic[] = [];
127
132
  if (!dir || !nodeFileSystem.exists(join(dir, "release.json"))) {
128
- diagnostics.push(
129
- createDiagnostic({
130
- severity: "error",
133
+ if (options.allowMissingLocalRelease) {
134
+ diagnostics.push(createDiagnostic({
135
+ severity: "warning",
131
136
  code: FORGE_RELEASE_ID_MISSING,
132
- message: "no prepared local release found",
133
- }),
134
- );
137
+ message: "no prepared local release found; treating this as not-prepared because --allow-missing-local-release was provided",
138
+ fixHint: "Run forge release prepare when you want to validate a concrete release bundle.",
139
+ suggestedCommands: releasePrepareNextActions(),
140
+ }));
141
+ } else {
142
+ diagnostics.push(
143
+ createDiagnostic({
144
+ severity: "error",
145
+ code: FORGE_RELEASE_ID_MISSING,
146
+ message: "no prepared local release found; run forge release prepare before release check/finalize",
147
+ fixHint: "Prepare a local release first, then re-run the release gate.",
148
+ suggestedCommands: releasePrepareNextActions(),
149
+ }),
150
+ );
151
+ }
135
152
  }
153
+ const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
136
154
  return {
137
- ok: diagnostics.length === 0,
138
- data: { releaseDir: dir },
155
+ ok: errors.length === 0,
156
+ data: { releaseDir: dir, state: diagnostics.length === 0 ? "prepared" : "missing-prepared-release" },
139
157
  diagnostics,
140
- exitCode: diagnostics.length === 0 ? 0 : 1,
158
+ nextActions: diagnostics.length === 0 ? releaseReadyNextActions() : releasePrepareNextActions(),
159
+ failureKind: errors.length === 0 ? undefined : "missing-prepared-release",
160
+ exitCode: errors.length === 0 ? 0 : 1,
141
161
  };
142
162
  }
143
163
 
@@ -256,7 +276,7 @@ export async function runReleaseCommand(
256
276
  };
257
277
  }
258
278
 
259
- return { ok: false, diagnostics: [], exitCode: 1 };
279
+ return { ok: false, diagnostics: [], failureKind: "unsupported-release-command", exitCode: 1 };
260
280
  }
261
281
 
262
282
  function missingRelease(): ReleaseCommandResult {
@@ -266,9 +286,13 @@ function missingRelease(): ReleaseCommandResult {
266
286
  createDiagnostic({
267
287
  severity: "error",
268
288
  code: FORGE_RELEASE_ID_MISSING,
269
- message: "release not found",
289
+ message: "release not found; pass --release <id> or run forge release prepare first",
290
+ fixHint: "Inspect the prepared releases directory or create a new local release.",
291
+ suggestedCommands: ["forge release prepare --env production", "forge release inspect --json"],
270
292
  }),
271
293
  ],
294
+ nextActions: ["forge release prepare --env production", "forge release inspect --json"],
295
+ failureKind: "missing-prepared-release",
272
296
  exitCode: 1,
273
297
  };
274
298
  }
@@ -0,0 +1,151 @@
1
+ import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { dirname, resolve } from "node:path";
3
+ import { readRepositoryManifest, resolveRepositoryRoot, validateRepositoryManifest } from "../repository-manifest/index.ts";
4
+ import { discoverRepository } from "../repository-analysis/scanner.ts";
5
+ import { analyzeRepository } from "../repository-analysis/analyze.ts";
6
+ import { readRepositorySnapshot, repositoryContext } from "../repository-analysis/context.ts";
7
+ import { evaluateRepositoryQuality } from "../repository-analysis/quality.ts";
8
+ import { isSensitiveRepositoryPath } from "../repository-analysis/scanner.ts";
9
+ import { collectRepositoryCache } from "../repository-analysis/cache-gc.ts";
10
+ import { createRepositoryAgentBenchmarkPlan, evaluateRepositoryAgentBenchmark, repositoryAgentBenchmarkBinding } from "../repository-analysis/benchmark.ts";
11
+ import { selectRepositoryChecks } from "../repository-analysis/check-selection.ts";
12
+ import { isDeepStrictEqual } from "node:util";
13
+ import { planRepositoryRuntime, observeRepositoryRuntime, readRuntimeObservation, selectRuntimeObservation, runtimeSourceDigest } from "../repository-analysis/runtime-observation.ts";
14
+
15
+ function readBenchmarkJson(cwd: string, input: string, maximum: number): any {
16
+ const path = resolve(cwd, input);
17
+ for (let current = path; ; current = dirname(current)) { if (existsSync(current) && lstatSync(current).isSymbolicLink()) throw new Error("Benchmark inputs cannot traverse symlinks"); if (dirname(current) === current) break; }
18
+ const stat = lstatSync(path);
19
+ if (!stat.isFile() || stat.size > maximum || isSensitiveRepositoryPath(path)) throw new Error("Benchmark input must be a bounded regular JSON file");
20
+ try { return JSON.parse(readFileSync(path, "utf8")); } catch { throw new Error("Benchmark input must contain valid JSON"); }
21
+ }
22
+
23
+ export interface RepositoryCliOptions {
24
+ action: "discover" | "analyze" | "context" | "quality" | "cache-gc" | "benchmark-plan" | "benchmark-report" | "runtime-plan" | "runtime-observe" | "runtime-context";
25
+ cwd: string;
26
+ root?: string;
27
+ projectId?: string;
28
+ manifestPath?: string;
29
+ cacheRoot?: string;
30
+ write: boolean;
31
+ json: boolean;
32
+ query?: string;
33
+ snapshotId?: string;
34
+ limit?: number;
35
+ maxChars?: number;
36
+ cursor?: string;
37
+ output?: string;
38
+ cases?: string;
39
+ modelConfig?: string;
40
+ repetitions?: number;
41
+ plan?: string;
42
+ observations?: string;
43
+ graceHours?: number;
44
+ execute?: boolean;
45
+ environmentId?: string;
46
+ observationId?: string;
47
+ }
48
+
49
+ export async function runRepositoryCommand(options: RepositoryCliOptions): Promise<{ exitCode: number; [key: string]: unknown }> {
50
+ try {
51
+ if (!["discover", "analyze", "context", "quality", "cache-gc", "benchmark-plan", "benchmark-report", "runtime-plan", "runtime-observe", "runtime-context"].includes(options.action)) throw new Error("Unsupported repository action");
52
+ const runtime = options.action.startsWith("runtime-");
53
+ if (!runtime && (options.execute || options.environmentId !== undefined || options.observationId !== undefined)) throw new Error("Runtime options require a runtime action");
54
+ if (options.execute && options.action !== "runtime-observe") throw new Error("--execute requires runtime-observe");
55
+ if (["runtime-plan", "runtime-observe"].includes(options.action) && !options.environmentId) throw new Error("Runtime planning/observation requires --environment-id <label>");
56
+ if (options.action === "runtime-observe" && !options.execute) throw new Error("Runtime observation executes declared commands; review runtime-plan then supply --execute");
57
+ if (["runtime-plan", "runtime-context"].includes(options.action) && options.write) throw new Error("Runtime plans/context are read-only");
58
+ if (runtime && options.cacheRoot) throw new Error("Runtime reports use the repository-owned cache; --cache-root is unsupported");
59
+ if (runtime && options.cursor) throw new Error("Runtime context does not support --cursor");
60
+ if (runtime && options.maxChars !== undefined && options.maxChars > 16000) throw new Error("Runtime context is bounded to 16000 characters");
61
+ if (runtime && options.limit !== undefined) throw new Error("Runtime actions do not support --limit");
62
+ if (options.action !== "runtime-context" && runtime && options.query !== undefined) throw new Error("--query requires runtime-context for runtime actions");
63
+ if (options.action === "runtime-context" && options.observationId) throw new Error("--observation-id requires runtime-plan or runtime-observe");
64
+ if (["context", "quality", "benchmark-plan", "benchmark-report"].includes(options.action) && options.write) throw new Error("Repository context/quality/benchmark is read-only");
65
+ if (["quality", "benchmark-plan"].includes(options.action) && !options.cases) throw new Error("Repository quality/benchmark-plan requires --cases <reviewed-cases.json>");
66
+ if (!["quality", "benchmark-plan"].includes(options.action) && options.cases) throw new Error("--cases is only supported for quality/benchmark-plan");
67
+ if (options.action === "benchmark-plan" && !options.modelConfig) throw new Error("Benchmark planning requires --model-config <file>");
68
+ if (options.action !== "benchmark-plan" && (options.modelConfig || options.repetitions !== undefined)) throw new Error("Model options require benchmark-plan");
69
+ if (options.action === "benchmark-report" && (!options.plan || !options.observations)) throw new Error("Benchmark reporting requires --plan and --observations");
70
+ if (options.action !== "benchmark-report" && (options.plan || options.observations)) throw new Error("--plan/--observations require benchmark-report");
71
+ if (options.action !== "cache-gc" && options.graceHours !== undefined) throw new Error("--grace-hours requires cache-gc");
72
+ if (options.action === "cache-gc" && [options.query, options.snapshotId, options.limit, options.maxChars, options.cursor].some(value => value !== undefined)) throw new Error("cache-gc does not support context or snapshot guard options");
73
+ if (options.output && (options.action !== "discover" || !options.write)) throw new Error("--output requires discovery with --write");
74
+ const root = await resolveRepositoryRoot(options);
75
+ if (options.action === "cache-gc") return { exitCode: 0, ok: true, ...collectRepositoryCache(root, { cacheRoot: options.cacheRoot, apply: options.write, graceMs: options.graceHours === undefined ? undefined : options.graceHours * 3600000 }) };
76
+ if (options.action === "discover") {
77
+ const manifest = discoverRepository(root);
78
+ const checked = validateRepositoryManifest(manifest);
79
+ if (!checked.manifest) throw new Error(`No valid supported repository proposal: ${checked.diagnostics.join("; ")}`);
80
+ if (options.write) {
81
+ const file = resolve(options.output ?? resolve(root, "forge.manifest.json"));
82
+ if (existsSync(file)) throw new Error("Discovery never overwrites an existing manifest; choose a new --output");
83
+ mkdirSync(dirname(file), { recursive: true });
84
+ writeFileSync(file, `${JSON.stringify(manifest, null, 2)}\n`, { flag: "wx" });
85
+ }
86
+ return { exitCode: 0, ok: true, root, manifest, wroteManifest: options.write };
87
+ }
88
+ const loaded = readRepositoryManifest(root, { manifestPath: options.manifestPath });
89
+ if (!loaded.manifest || loaded.diagnostics.length) throw new Error(loaded.diagnostics.join("; ") || "Repository manifest missing. Run forge manifest discover, review and save the proposal first.");
90
+ if (options.action === "runtime-observe") {
91
+ if (options.snapshotId) {
92
+ const input = await analyzeRepository(root, loaded.manifest, { write: false });
93
+ if (input.snapshotId !== options.snapshotId) throw new Error("Runtime snapshot guard differs from current source");
94
+ }
95
+ const report = await observeRepositoryRuntime(root, loaded.manifest, { environmentId: options.environmentId!, observationId: options.observationId, write: options.write });
96
+ const ok = report.observations.every(item => item.status === "completed");
97
+ return { ok, exitCode: ok ? 0 : 1, wroteArtifacts: options.write, report };
98
+ }
99
+ if (options.action === "runtime-plan") {
100
+ const input = await analyzeRepository(root, loaded.manifest, { write: false });
101
+ if (options.snapshotId && input.snapshotId !== options.snapshotId) throw new Error("Runtime snapshot guard differs from current source");
102
+ return { ok: true, exitCode: 0, plan: { ...planRepositoryRuntime(input, options.environmentId!, options.observationId), inputDigest: runtimeSourceDigest(root, loaded.manifest) } };
103
+ }
104
+ if (options.action === "analyze") {
105
+ const snapshot = await analyzeRepository(root, loaded.manifest, { write: options.write, cacheRoot: options.cacheRoot });
106
+ const ok = snapshot.coverage.errors === 0;
107
+ return { exitCode: ok ? 0 : 1, ok, root, wroteArtifacts: options.write, snapshotId: snapshot.snapshotId, coverage: snapshot.coverage, snapshot };
108
+ }
109
+ const snapshot = readRepositorySnapshot(root, { cacheRoot: options.cacheRoot });
110
+ if (!snapshot) throw new Error("Repository snapshot missing; run forge repository analyze --write first");
111
+ if (options.action === "runtime-context") {
112
+ const current = repositoryContext(root, snapshot, "overview", { manifest: loaded.manifest, snapshotId: options.snapshotId });
113
+ if (!current.ok) throw new Error(current.diagnostics.join("; "));
114
+ const report = readRuntimeObservation(root, snapshot, { environmentId: options.environmentId });
115
+ if (!report) throw new Error("Runtime report missing, stale or invalid; review runtime-plan and run runtime-observe explicitly");
116
+ return { ok: true, exitCode: 0, context: selectRuntimeObservation(report, { query: options.query, maxChars: options.maxChars }) };
117
+ }
118
+ if (["quality", "benchmark-plan", "benchmark-report"].includes(options.action)) {
119
+ const current = repositoryContext(root, snapshot, "overview", { manifest: loaded.manifest, snapshotId: options.snapshotId });
120
+ if (!current.ok) throw new Error(current.diagnostics.join("; "));
121
+ if (options.action === "benchmark-report") {
122
+ const input = readBenchmarkJson(options.cwd, options.plan!, 128 * 1024 * 1024), plan = input.plan ?? input;
123
+ const report = evaluateRepositoryAgentBenchmark(plan, readBenchmarkJson(options.cwd, options.observations!, 16 * 1024 * 1024));
124
+ const expectedBinding = repositoryAgentBenchmarkBinding(snapshot);
125
+ if (!isDeepStrictEqual(plan.binding, expectedBinding)) throw new Error("Benchmark plan belongs to a different repository input");
126
+ const checksByScope = new Map<string, unknown>();
127
+ for (const run of plan.runs) {
128
+ const key = JSON.stringify(run.workerInput.scope);
129
+ if (!checksByScope.has(key)) checksByScope.set(key, JSON.parse(JSON.stringify(selectRepositoryChecks(snapshot, { scope: run.workerInput.scope }).checks)));
130
+ if (!isDeepStrictEqual(run.workerInput.checks, checksByScope.get(key))) throw new Error("Benchmark checks differ from current repository declarations");
131
+ }
132
+ return { ...report, ok: true, exitCode: 0 };
133
+ }
134
+ if (options.action === "benchmark-plan") {
135
+ const plan = createRepositoryAgentBenchmarkPlan(snapshot, readBenchmarkJson(options.cwd, options.cases!, 128 * 1024), { model: readBenchmarkJson(options.cwd, options.modelConfig!, 128 * 1024), repetitions: options.repetitions, maxChars: options.maxChars });
136
+ return { plan, ok: true, exitCode: 0 };
137
+ }
138
+ const casesPath = resolve(options.cwd, options.cases!);
139
+ const stat = lstatSync(casesPath);
140
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 128 * 1024 || isSensitiveRepositoryPath(casesPath)) throw new Error("Quality cases must be a bounded regular JSON file");
141
+ let cases: unknown;
142
+ try { cases = JSON.parse(readFileSync(casesPath, "utf8")); } catch { throw new Error("Quality cases must contain valid JSON"); }
143
+ const report = evaluateRepositoryQuality(snapshot, cases as Parameters<typeof evaluateRepositoryQuality>[1], { maxChars: options.maxChars });
144
+ return { ...report, ok: report.passed, exitCode: report.passed ? 0 : 1 };
145
+ }
146
+ const result = repositoryContext(root, snapshot, options.query ?? "overview", {
147
+ snapshotId: options.snapshotId, limit: options.limit, maxChars: options.maxChars, cursor: options.cursor, manifest: loaded.manifest,
148
+ });
149
+ return { ...result, exitCode: result.ok ? 0 : 1 };
150
+ } catch (error) { return { exitCode: 1, ok: false, diagnostics: [{ severity: "error", code: "FORGE_REPOSITORY", message: (error instanceof Error ? error.message : "Repository operation failed").slice(0, 512) }] }; }
151
+ }
@@ -1,6 +1,11 @@
1
1
  import { listEntries, runEntry } from "../runtime/executor.ts";
2
2
  import { createDiagnostic } from "../compiler/diagnostics/create.ts";
3
3
  import type { RunEntryResult, ListEntriesResult } from "../runtime/executor.ts";
4
+ import {
5
+ resolveExternalQualifiedName,
6
+ runExternalEntry,
7
+ } from "../runtime/external/bridge.ts";
8
+ import { resolveAuthFromCli } from "../runtime/auth/resolve.ts";
4
9
 
5
10
  export interface RunCommandOptions {
6
11
  name?: string;
@@ -10,6 +15,7 @@ export interface RunCommandOptions {
10
15
  userId?: string;
11
16
  tenantId?: string;
12
17
  role?: string;
18
+ args?: unknown;
13
19
  workspaceRoot: string;
14
20
  }
15
21
 
@@ -29,9 +35,45 @@ export async function runRunCommand(
29
35
  return { list, exitCode: list.exitCode };
30
36
  }
31
37
 
38
+ const external = resolveExternalQualifiedName(options.workspaceRoot, options.name!, "command");
39
+ if (external) {
40
+ const run = await runExternalEntry(options.workspaceRoot, {
41
+ kind: "command",
42
+ serviceName: external.serviceName,
43
+ entryName: external.entryName,
44
+ args: options.args,
45
+ auth: resolveAuthFromCli({
46
+ userId: options.userId,
47
+ tenantId: options.tenantId,
48
+ role: options.role,
49
+ }),
50
+ });
51
+ return {
52
+ run: {
53
+ ok: run.ok,
54
+ result: run.result,
55
+ diagnostics: run.diagnostics,
56
+ exitCode: run.exitCode,
57
+ traceId: run.traceId,
58
+ entry: {
59
+ id: `external:${external.serviceName}:command:${external.entryName}`,
60
+ kind: "command",
61
+ name: `${external.serviceName}.${external.entryName}`,
62
+ qualifiedName: `${external.serviceName}.${external.entryName}`,
63
+ file: `external:${external.serviceName}`,
64
+ moduleId: `external:${external.serviceName}`,
65
+ runtimeContext: "command",
66
+ dependencies: [],
67
+ },
68
+ },
69
+ exitCode: run.exitCode,
70
+ };
71
+ }
72
+
32
73
  const run = await runEntry(options.workspaceRoot, options.name!, {
33
74
  json: options.json,
34
75
  mock: options.mock,
76
+ args: options.args,
35
77
  userId: options.userId,
36
78
  tenantId: options.tenantId,
37
79
  role: options.role,
@@ -8,6 +8,7 @@ import {
8
8
  } from "../runtime/secrets/check.ts";
9
9
  import { getRuntimeEnvStore, initializeRuntimeEnv } from "../runtime/context/create-context.ts";
10
10
  import { redactSecretValue } from "../runtime/secrets/env-loader.ts";
11
+ import { productionPlaceholderEnvProblems } from "./env-placeholders.ts";
11
12
 
12
13
  export type SecretsSubcommand = "list" | "check" | "print" | "set" | "unset" | "prove";
13
14
 
@@ -219,13 +220,146 @@ export function formatSecretsJson(result: SecretsCommandResult): string {
219
220
  return `${JSON.stringify(result, null, 2)}\n`;
220
221
  }
221
222
 
222
- export type EnvSubcommand = "list" | "check" | "print";
223
+ export type EnvSubcommand = "list" | "check" | "print" | "doctor";
224
+ export type EnvDoctorTarget = "local" | "staging" | "production";
223
225
 
224
226
  export interface EnvCommandOptions {
225
227
  subcommand: EnvSubcommand;
226
228
  workspaceRoot: string;
227
229
  json: boolean;
228
230
  redacted?: boolean;
231
+ target?: EnvDoctorTarget;
232
+ }
233
+
234
+ const ENV_DOCTOR_KEYS = [
235
+ "DATABASE_URL",
236
+ "FORGE_AUTH_MODE",
237
+ "FORGE_AUTH_ISSUER",
238
+ "FORGE_AUTH_AUDIENCE",
239
+ "FORGE_AUTH_JWKS_URI",
240
+ "FORGE_AUTH_DISCOVERY_URL",
241
+ "WORKOS_API_KEY",
242
+ "WORKOS_CLIENT_ID",
243
+ "WORKOS_COOKIE_PASSWORD",
244
+ "WORKOS_REDIRECT_URI",
245
+ "WORKOS_POST_LOGIN_REDIRECT_URI",
246
+ "WORKOS_POST_LOGOUT_REDIRECT_URI",
247
+ "WORKOS_WEBHOOK_SECRET",
248
+ ] as const;
249
+
250
+ function parseEnvFile(workspaceRoot: string, relative: string): Record<string, string> {
251
+ const path = join(workspaceRoot, relative);
252
+ if (!nodeFileSystem.exists(path)) return {};
253
+ const values: Record<string, string> = {};
254
+ for (const rawLine of (nodeFileSystem.readText(path) ?? "").split(/\r?\n/)) {
255
+ const line = rawLine.trim();
256
+ if (!line || line.startsWith("#")) continue;
257
+ const eq = line.indexOf("=");
258
+ if (eq <= 0) continue;
259
+ const rawValue = line.slice(eq + 1).trim();
260
+ values[line.slice(0, eq).trim()] = rawValue.replace(/^["']|["']$/g, "");
261
+ }
262
+ return values;
263
+ }
264
+
265
+ function envDoctorFiles(target: EnvDoctorTarget): string[] {
266
+ if (target === "production") return ["deploy/.env.production"];
267
+ if (target === "staging") return ["deploy/.env.staging", ".env.staging"];
268
+ return [".env", ".env.local"];
269
+ }
270
+
271
+ function hasWorkOSAppArtifacts(workspaceRoot: string): boolean {
272
+ return nodeFileSystem.exists(join(workspaceRoot, "workos-seed.yml")) ||
273
+ nodeFileSystem.exists(join(workspaceRoot, "src/policies.workos.ts")) ||
274
+ nodeFileSystem.exists(join(workspaceRoot, "src/forge/_generated/integrations/workos/auth-routes.ts"));
275
+ }
276
+
277
+ function envDoctor(options: EnvCommandOptions): SecretsCommandResult {
278
+ const target = options.target ?? "local";
279
+ const files = envDoctorFiles(target).map((path) => {
280
+ const values = parseEnvFile(options.workspaceRoot, path);
281
+ return {
282
+ path,
283
+ present: nodeFileSystem.exists(join(options.workspaceRoot, path)),
284
+ keys: Object.keys(values).filter((key) => ENV_DOCTOR_KEYS.includes(key as (typeof ENV_DOCTOR_KEYS)[number])).sort(),
285
+ values,
286
+ };
287
+ });
288
+ const processValues = Object.fromEntries(
289
+ ENV_DOCTOR_KEYS.filter((key) => Boolean(process.env[key])).map((key) => [key, process.env[key]!]),
290
+ );
291
+ const effective = {
292
+ ...Object.assign({}, ...files.map((file) => file.values)),
293
+ ...processValues,
294
+ } as Record<string, string>;
295
+ const required = target === "production"
296
+ ? ["DATABASE_URL", "FORGE_AUTH_MODE", "FORGE_AUTH_ISSUER", "FORGE_AUTH_AUDIENCE"]
297
+ : ["FORGE_AUTH_MODE"];
298
+ const missing = required.filter((key) => !effective[key]);
299
+ const authMode = effective.FORGE_AUTH_MODE ?? "dev-headers";
300
+ const productionAuth = authMode === "jwt" || authMode === "oidc";
301
+ const database = effective.DATABASE_URL
302
+ ? "postgres"
303
+ : target === "production"
304
+ ? "missing"
305
+ : "local-dev";
306
+ const workosDetected = hasWorkOSAppArtifacts(options.workspaceRoot) ||
307
+ Boolean(effective.WORKOS_CLIENT_ID || effective.WORKOS_API_KEY);
308
+ const provider = workosDetected ? "workos" : "none";
309
+ const hasJwtSource = Boolean(effective.FORGE_AUTH_JWKS_URI || effective.FORGE_AUTH_DISCOVERY_URL);
310
+ const placeholderProblems = target === "production"
311
+ ? productionPlaceholderEnvProblems(effective, ENV_DOCTOR_KEYS as unknown as string[])
312
+ : [];
313
+ const blockers = [
314
+ ...missing.filter((key) => key !== "DATABASE_URL").map((key) => `${key} missing`),
315
+ ...(target === "production" && !productionAuth ? [`FORGE_AUTH_MODE=${authMode} is not production auth`] : []),
316
+ ...(target === "production" && database === "missing" ? ["DATABASE_URL missing"] : []),
317
+ ...(target === "production" && productionAuth && !hasJwtSource
318
+ ? ["FORGE_AUTH_JWKS_URI or FORGE_AUTH_DISCOVERY_URL missing"]
319
+ : []),
320
+ ...(target === "production" && workosDetected && !effective.WORKOS_CLIENT_ID ? ["WORKOS_CLIENT_ID missing"] : []),
321
+ ...(target === "production" && workosDetected && !effective.WORKOS_API_KEY ? ["WORKOS_API_KEY missing"] : []),
322
+ ...placeholderProblems.map((problem) => `${problem.key} uses placeholder value (${problem.reason})`),
323
+ ];
324
+ const warnings = [
325
+ ...(target === "production" && files.every((file) => !file.present)
326
+ ? ["deploy/.env.production not found; process.env alone may be fine in CI but is easy for agents to miss"]
327
+ : []),
328
+ ...(workosDetected && !effective.WORKOS_COOKIE_PASSWORD ? ["WORKOS_COOKIE_PASSWORD missing"] : []),
329
+ ...(workosDetected && !effective.WORKOS_REDIRECT_URI ? ["WORKOS_REDIRECT_URI missing"] : []),
330
+ ];
331
+ return {
332
+ exitCode: blockers.length === 0 ? 0 : 1,
333
+ data: {
334
+ schemaVersion: "0.1.0",
335
+ kind: "env-doctor",
336
+ ok: blockers.length === 0,
337
+ target,
338
+ authMode,
339
+ productionAuth,
340
+ database,
341
+ provider,
342
+ sources: [
343
+ {
344
+ path: "process.env",
345
+ present: Object.keys(processValues).length > 0,
346
+ keys: Object.keys(processValues).sort(),
347
+ values: undefined,
348
+ },
349
+ ...files.map(({ path, present, keys }) => ({ path, present, keys })),
350
+ ],
351
+ present: ENV_DOCTOR_KEYS.filter((key) => Boolean(effective[key])).sort(),
352
+ missing,
353
+ placeholderProblems,
354
+ blockers,
355
+ warnings,
356
+ nextActions: blockers.length === 0
357
+ ? ["forge deploy readiness --production --json"]
358
+ : target === "production"
359
+ ? ["cp deploy/.env.production.example deploy/.env.production", "forge env doctor --target production --json"]
360
+ : ["forge env doctor --target local --json"],
361
+ },
362
+ };
229
363
  }
230
364
 
231
365
  export async function runEnvCommand(options: EnvCommandOptions): Promise<SecretsCommandResult> {
@@ -233,6 +367,8 @@ export async function runEnvCommand(options: EnvCommandOptions): Promise<Secrets
233
367
  const schema = loadEnvSchema(options.workspaceRoot);
234
368
 
235
369
  switch (options.subcommand) {
370
+ case "doctor":
371
+ return envDoctor(options);
236
372
  case "list":
237
373
  if (!schema) {
238
374
  return {