forgeos 0.1.0-alpha.4 → 0.1.0-alpha.40

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 (415) hide show
  1. package/.npmignore +10 -0
  2. package/AGENTS.md +142 -90
  3. package/CHANGELOG.md +418 -0
  4. package/LICENSE +21 -0
  5. package/README.md +67 -8
  6. package/adapters/go/README.md +23 -0
  7. package/adapters/go/go.mod +3 -0
  8. package/adapters/go/http.go +149 -0
  9. package/adapters/go/registry.go +234 -0
  10. package/adapters/go/types.go +136 -0
  11. package/adapters/java/README.md +68 -0
  12. package/adapters/java/pom.xml +34 -0
  13. package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
  14. package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
  15. package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
  16. package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
  17. package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
  18. package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
  19. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
  20. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
  21. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
  22. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
  23. package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
  24. package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
  25. package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
  26. package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
  27. package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
  28. package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
  29. package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
  30. package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
  31. package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
  32. package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
  33. package/adapters/java-spring-boot-starter/README.md +32 -0
  34. package/adapters/java-spring-boot-starter/pom.xml +36 -0
  35. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
  36. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
  37. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
  38. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
  39. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
  40. package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
  41. package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
  42. package/bin/forge.mjs +18 -0
  43. package/docs/cair-protocol.md +103 -0
  44. package/docs/changelog.md +534 -0
  45. package/docs/forge-protocol.md +189 -0
  46. package/examples/go-billing/go.mod +7 -0
  47. package/examples/go-billing/main.go +120 -0
  48. package/examples/java-billing/pom.xml +52 -0
  49. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
  50. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
  51. package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
  52. package/package.json +31 -3
  53. package/schemas/forge-manifest.schema.json +57 -0
  54. package/src/forge/_generated/releaseManifest.json +1 -2
  55. package/src/forge/_generated/releaseManifest.ts +3 -3
  56. package/src/forge/agent-adapters/index.ts +1584 -125
  57. package/src/forge/agent-adapters/types.ts +224 -1
  58. package/src/forge/agent-memory/bridge.ts +1333 -0
  59. package/src/forge/agent-memory/context-pack.ts +277 -0
  60. package/src/forge/agent-memory/hook-runner.ts +312 -0
  61. package/src/forge/agent-memory/mcp.ts +224 -0
  62. package/src/forge/agent-memory/normalize.ts +498 -0
  63. package/src/forge/agent-memory/redaction.ts +103 -0
  64. package/src/forge/agent-memory/sources/claude-code.ts +51 -0
  65. package/src/forge/agent-memory/sources/codex-hook-runner.mjs +273 -0
  66. package/src/forge/agent-memory/sources/codex.ts +119 -0
  67. package/src/forge/agent-memory/sources/cursor.ts +35 -0
  68. package/src/forge/agent-memory/types.ts +204 -0
  69. package/src/forge/bench.ts +248 -0
  70. package/src/forge/brownfield-import/index.ts +801 -0
  71. package/src/forge/brownfield-import/types.ts +127 -0
  72. package/src/forge/cair/action-journal.ts +61 -0
  73. package/src/forge/cair/action-parser.ts +314 -0
  74. package/src/forge/cair/action-validator.ts +40 -0
  75. package/src/forge/cair/actions.ts +1818 -0
  76. package/src/forge/cair/format.ts +77 -0
  77. package/src/forge/cair/index.ts +106 -0
  78. package/src/forge/cair/query.ts +478 -0
  79. package/src/forge/cair/snapshot.ts +315 -0
  80. package/src/forge/cair/types.ts +248 -0
  81. package/src/forge/cli/ai.ts +339 -21
  82. package/src/forge/cli/auth.ts +348 -10
  83. package/src/forge/cli/authmd.ts +441 -0
  84. package/src/forge/cli/baseline.ts +112 -0
  85. package/src/forge/cli/build.ts +20 -4
  86. package/src/forge/cli/changed.ts +437 -0
  87. package/src/forge/cli/codex-app-server.ts +877 -0
  88. package/src/forge/cli/commands.ts +1620 -20
  89. package/src/forge/cli/db.ts +209 -2
  90. package/src/forge/cli/deploy.ts +887 -0
  91. package/src/forge/cli/deps.ts +82 -13
  92. package/src/forge/cli/dev.ts +961 -46
  93. package/src/forge/cli/docs.ts +265 -0
  94. package/src/forge/cli/doctor.ts +297 -0
  95. package/src/forge/cli/field-test.ts +329 -0
  96. package/src/forge/cli/handoff.ts +336 -0
  97. package/src/forge/cli/index.ts +1 -0
  98. package/src/forge/cli/last-run.ts +84 -0
  99. package/src/forge/cli/main.ts +107 -3
  100. package/src/forge/cli/new.ts +152 -13
  101. package/src/forge/cli/next-actions.ts +23 -0
  102. package/src/forge/cli/output.ts +389 -6
  103. package/src/forge/cli/parse.ts +1050 -34
  104. package/src/forge/cli/progress.ts +51 -0
  105. package/src/forge/cli/query.ts +32 -0
  106. package/src/forge/cli/release.ts +35 -11
  107. package/src/forge/cli/rls.ts +43 -4
  108. package/src/forge/cli/run.ts +41 -0
  109. package/src/forge/cli/security.ts +112 -0
  110. package/src/forge/cli/self-host.ts +56 -14
  111. package/src/forge/cli/studio.ts +2218 -0
  112. package/src/forge/cli/verify.ts +1455 -32
  113. package/src/forge/cli/windows.ts +23 -0
  114. package/src/forge/cli/workos.ts +1020 -0
  115. package/src/forge/compiler/agent-contract/build.ts +670 -64
  116. package/src/forge/compiler/agent-contract/types.ts +45 -2
  117. package/src/forge/compiler/ai-registry/parse.ts +71 -2
  118. package/src/forge/compiler/api-surface/build.ts +47 -0
  119. package/src/forge/compiler/app-graph/build.ts +112 -9
  120. package/src/forge/compiler/app-graph/extract.ts +107 -0
  121. package/src/forge/compiler/app-graph/module-graph.ts +73 -78
  122. package/src/forge/compiler/app-graph/parser.ts +24 -24
  123. package/src/forge/compiler/app-graph/profile.ts +26 -0
  124. package/src/forge/compiler/app-graph/versions.ts +1 -1
  125. package/src/forge/compiler/classifier/capabilities.ts +3 -2
  126. package/src/forge/compiler/classifier/classify.ts +32 -8
  127. package/src/forge/compiler/classifier/secrets.ts +3 -2
  128. package/src/forge/compiler/classifier/signals.ts +91 -1
  129. package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
  130. package/src/forge/compiler/client-sdk/render-client.ts +295 -13
  131. package/src/forge/compiler/data-graph/parse.ts +20 -4
  132. package/src/forge/compiler/data-graph/sql/ddl.ts +144 -31
  133. package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
  134. package/src/forge/compiler/data-graph/sql/types.ts +1 -0
  135. package/src/forge/compiler/diagnostics/codes.ts +30 -0
  136. package/src/forge/compiler/diagnostics/create.ts +21 -2
  137. package/src/forge/compiler/diagnostics/index.ts +2 -0
  138. package/src/forge/compiler/emitter/barrel.ts +3 -0
  139. package/src/forge/compiler/emitter/render.ts +9 -0
  140. package/src/forge/compiler/external-manifest/registry.ts +205 -0
  141. package/src/forge/compiler/external-manifest/types.ts +91 -0
  142. package/src/forge/compiler/external-manifest/validate.ts +373 -0
  143. package/src/forge/compiler/frontend-graph/build.ts +142 -14
  144. package/src/forge/compiler/integration/add.ts +618 -24
  145. package/src/forge/compiler/integration/plan.ts +43 -12
  146. package/src/forge/compiler/integration/render.ts +29 -0
  147. package/src/forge/compiler/integration/snapshot.ts +2 -0
  148. package/src/forge/compiler/integration/templates/convex.ts +70 -0
  149. package/src/forge/compiler/integration/templates/index.ts +1 -0
  150. package/src/forge/compiler/integration/templates/render.ts +38 -0
  151. package/src/forge/compiler/integration/templates/types.ts +3 -0
  152. package/src/forge/compiler/integration/templates/workos.ts +1219 -0
  153. package/src/forge/compiler/make-registry/build.ts +8 -9
  154. package/src/forge/compiler/orchestrator/fast-check.ts +4 -0
  155. package/src/forge/compiler/orchestrator/generate-lock.ts +14 -3
  156. package/src/forge/compiler/orchestrator/manifest.ts +1 -1
  157. package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
  158. package/src/forge/compiler/orchestrator/plan.ts +247 -10
  159. package/src/forge/compiler/orchestrator/profile.ts +65 -0
  160. package/src/forge/compiler/orchestrator/run.ts +97 -31
  161. package/src/forge/compiler/orchestrator/serialize.ts +33 -8
  162. package/src/forge/compiler/orchestrator/types.ts +1 -1
  163. package/src/forge/compiler/package-graph/compiler.ts +3 -1
  164. package/src/forge/compiler/package-graph/constants.ts +1 -1
  165. package/src/forge/compiler/package-manager/adapter.ts +4 -1
  166. package/src/forge/compiler/package-manager/commands.ts +4 -0
  167. package/src/forge/compiler/package-manager/executor.ts +30 -1
  168. package/src/forge/compiler/package-upgrades/planner.ts +155 -18
  169. package/src/forge/compiler/package-upgrades/types.ts +2 -0
  170. package/src/forge/compiler/policy-registry/build.ts +47 -2
  171. package/src/forge/compiler/policy-registry/parse.ts +32 -2
  172. package/src/forge/compiler/recipes/definitions.ts +63 -0
  173. package/src/forge/compiler/recipes/index.ts +2 -0
  174. package/src/forge/compiler/recipes/registry.ts +6 -0
  175. package/src/forge/compiler/test-graph/build.ts +11 -3
  176. package/src/forge/compiler/types/app-graph.ts +8 -2
  177. package/src/forge/compiler/types/cli.ts +75 -1
  178. package/src/forge/compiler/types/dev-manifest.ts +4 -0
  179. package/src/forge/compiler/types/emit.ts +2 -0
  180. package/src/forge/compiler/types/frontend-graph.ts +2 -2
  181. package/src/forge/compiler/types/integration.ts +1 -0
  182. package/src/forge/compiler/types/package-graph.ts +6 -0
  183. package/src/forge/compiler/types/policy-registry.ts +3 -1
  184. package/src/forge/delta/classifier.ts +52 -0
  185. package/src/forge/delta/explain.ts +238 -0
  186. package/src/forge/delta/git-observer.ts +43 -0
  187. package/src/forge/delta/ids.ts +44 -0
  188. package/src/forge/delta/index.ts +25 -0
  189. package/src/forge/delta/recorder.ts +540 -0
  190. package/src/forge/delta/redaction.ts +50 -0
  191. package/src/forge/delta/schema.ts +240 -0
  192. package/src/forge/delta/session.ts +142 -0
  193. package/src/forge/delta/status.ts +1202 -0
  194. package/src/forge/delta/store.ts +3284 -0
  195. package/src/forge/delta/timeline.ts +178 -0
  196. package/src/forge/dev/server.ts +808 -17
  197. package/src/forge/dev/types.ts +15 -1
  198. package/src/forge/dev/watch.ts +17 -7
  199. package/src/forge/dev-console/cycle.ts +280 -33
  200. package/src/forge/dev-console/types.ts +47 -1
  201. package/src/forge/impact/index.ts +204 -9
  202. package/src/forge/impact/types.ts +33 -1
  203. package/src/forge/intent/index.ts +35 -16
  204. package/src/forge/make/fields.ts +26 -0
  205. package/src/forge/make/index.ts +40 -6
  206. package/src/forge/make/templates.ts +155 -2
  207. package/src/forge/make/types.ts +2 -1
  208. package/src/forge/policy.ts +1 -1
  209. package/src/forge/react/index.ts +160 -2
  210. package/src/forge/refactor/index.ts +1 -0
  211. package/src/forge/repair/rules/index.ts +2 -2
  212. package/src/forge/review/index.ts +158 -12
  213. package/src/forge/review/types.ts +15 -0
  214. package/src/forge/runtime/auth/config.ts +17 -0
  215. package/src/forge/runtime/auth/evaluate.ts +15 -2
  216. package/src/forge/runtime/auth/resolve.ts +61 -6
  217. package/src/forge/runtime/auth/types.ts +1 -0
  218. package/src/forge/runtime/db/factory.ts +1 -3
  219. package/src/forge/runtime/db/generated-client.ts +13 -2
  220. package/src/forge/runtime/db/memory-adapter.ts +139 -32
  221. package/src/forge/runtime/db/pglite-adapter.ts +263 -2
  222. package/src/forge/runtime/db/postgres-adapter.ts +6 -3
  223. package/src/forge/runtime/executor.ts +129 -16
  224. package/src/forge/runtime/external/bridge.ts +649 -0
  225. package/src/forge/runtime/runner/run-entry.ts +16 -7
  226. package/src/forge/runtime/telemetry/buffer.ts +4 -1
  227. package/src/forge/runtime/telemetry/scrubber.ts +35 -5
  228. package/src/forge/runtime/webhooks/security.ts +12 -7
  229. package/src/forge/server.ts +17 -3
  230. package/src/forge/ui/index.ts +209 -0
  231. package/src/forge/ui/types.ts +1 -0
  232. package/src/forge/version.ts +1 -1
  233. package/src/forge/vue/index.ts +422 -0
  234. package/src/forge/workspace/baseline.ts +112 -0
  235. package/src/forge/workspace/change-summary.ts +250 -0
  236. package/src/forge/workspace/forge-cli.ts +42 -0
  237. package/src/forge/workspace/git-summary.ts +391 -0
  238. package/templates/agent-workroom/AGENTS.md +29 -0
  239. package/templates/agent-workroom/README.md +34 -0
  240. package/templates/agent-workroom/forge.config.ts +3 -0
  241. package/templates/agent-workroom/package.json +33 -0
  242. package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
  243. package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
  244. package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
  245. package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
  246. package/templates/agent-workroom/src/forge/schema.ts +54 -0
  247. package/templates/agent-workroom/src/policies.ts +6 -0
  248. package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
  249. package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
  250. package/templates/agent-workroom/tsconfig.json +16 -0
  251. package/templates/agent-workroom/web/index.html +12 -0
  252. package/templates/agent-workroom/web/package.json +21 -0
  253. package/templates/agent-workroom/web/src/App.tsx +345 -0
  254. package/templates/agent-workroom/web/src/lib/forge.ts +13 -0
  255. package/templates/agent-workroom/web/src/main.tsx +13 -0
  256. package/templates/agent-workroom/web/src/styles.css +545 -0
  257. package/templates/agent-workroom/web/tsconfig.json +27 -0
  258. package/templates/b2b-support-web/package.json +1 -0
  259. package/templates/b2b-support-web/web/package.json +1 -1
  260. package/templates/minimal-web/package.json +1 -1
  261. package/templates/minimal-web/web/package.json +2 -2
  262. package/templates/nuxt-web/.vscode/settings.json +14 -0
  263. package/templates/nuxt-web/README.md +30 -0
  264. package/templates/nuxt-web/forge.config.ts +3 -0
  265. package/templates/nuxt-web/package.json +33 -0
  266. package/templates/nuxt-web/src/actions/logNoteCreated.ts +11 -0
  267. package/templates/nuxt-web/src/commands/createNote.ts +26 -0
  268. package/templates/nuxt-web/src/forge/schema.ts +12 -0
  269. package/templates/nuxt-web/src/policies.ts +6 -0
  270. package/templates/nuxt-web/src/queries/listNotes.ts +8 -0
  271. package/templates/nuxt-web/src/queries/liveNotes.ts +8 -0
  272. package/templates/nuxt-web/tsconfig.json +17 -0
  273. package/templates/nuxt-web/web/app.vue +67 -0
  274. package/templates/nuxt-web/web/components/LiveNotes.vue +89 -0
  275. package/templates/nuxt-web/web/components/NoteComposer.vue +100 -0
  276. package/templates/nuxt-web/web/composables/forge.ts +13 -0
  277. package/templates/nuxt-web/web/composables/useNotes.ts +24 -0
  278. package/templates/nuxt-web/web/nuxt.config.ts +11 -0
  279. package/templates/nuxt-web/web/package.json +18 -0
  280. package/templates/nuxt-web/web/plugins/forge.client.ts +10 -0
  281. package/templates/nuxt-web/web/plugins/forge.server.ts +10 -0
  282. package/templates/nuxt-web/web/server/api/forge-health.get.ts +7 -0
  283. package/templates/nuxt-web/web/tsconfig.json +6 -0
  284. package/src/forge/_generated/actionSubscriptions.json +0 -2
  285. package/src/forge/_generated/actionSubscriptions.ts +0 -10
  286. package/src/forge/_generated/agentAdapterManifest.json +0 -2
  287. package/src/forge/_generated/agentAdapterManifest.ts +0 -73
  288. package/src/forge/_generated/agentContract.json +0 -2
  289. package/src/forge/_generated/agentContract.ts +0 -7829
  290. package/src/forge/_generated/agentQuickstart.md +0 -34
  291. package/src/forge/_generated/agentTools.json +0 -2
  292. package/src/forge/_generated/agentTools.md +0 -16
  293. package/src/forge/_generated/agentTools.ts +0 -12
  294. package/src/forge/_generated/aiContext.ts +0 -125
  295. package/src/forge/_generated/aiModels.json +0 -2
  296. package/src/forge/_generated/aiModels.ts +0 -51
  297. package/src/forge/_generated/aiProviders.json +0 -2
  298. package/src/forge/_generated/aiProviders.ts +0 -23
  299. package/src/forge/_generated/aiRegistry.json +0 -2
  300. package/src/forge/_generated/aiRegistry.ts +0 -31
  301. package/src/forge/_generated/api.json +0 -2
  302. package/src/forge/_generated/api.ts +0 -8
  303. package/src/forge/_generated/appGraph.json +0 -2
  304. package/src/forge/_generated/appGraph.ts +0 -14919
  305. package/src/forge/_generated/appMap.md +0 -55
  306. package/src/forge/_generated/artifactManifest.json +0 -2
  307. package/src/forge/_generated/artifactManifest.ts +0 -7
  308. package/src/forge/_generated/authClaims.json +0 -2
  309. package/src/forge/_generated/authClaims.ts +0 -13
  310. package/src/forge/_generated/authConfig.json +0 -2
  311. package/src/forge/_generated/authConfig.ts +0 -17
  312. package/src/forge/_generated/authContext.ts +0 -23
  313. package/src/forge/_generated/authRegistry.json +0 -2
  314. package/src/forge/_generated/authRegistry.ts +0 -25
  315. package/src/forge/_generated/buildInfo.json +0 -2
  316. package/src/forge/_generated/buildInfo.ts +0 -9
  317. package/src/forge/_generated/capabilityMap.json +0 -2
  318. package/src/forge/_generated/capabilityMap.md +0 -15
  319. package/src/forge/_generated/capabilityMap.ts +0 -17
  320. package/src/forge/_generated/client.ts +0 -282
  321. package/src/forge/_generated/clientApi.ts +0 -9
  322. package/src/forge/_generated/clientManifest.json +0 -2
  323. package/src/forge/_generated/clientManifest.ts +0 -39
  324. package/src/forge/_generated/clientTypes.ts +0 -78
  325. package/src/forge/_generated/configRegistry.json +0 -2
  326. package/src/forge/_generated/configRegistry.ts +0 -4
  327. package/src/forge/_generated/dataGraph.json +0 -2
  328. package/src/forge/_generated/dataGraph.ts +0 -8
  329. package/src/forge/_generated/db.json +0 -2
  330. package/src/forge/_generated/db.ts +0 -2
  331. package/src/forge/_generated/dbSecurityManifest.json +0 -2
  332. package/src/forge/_generated/dbSecurityManifest.ts +0 -15
  333. package/src/forge/_generated/dbSessionContext.json +0 -2
  334. package/src/forge/_generated/dbSessionContext.ts +0 -39
  335. package/src/forge/_generated/deployManifest.json +0 -2
  336. package/src/forge/_generated/deployManifest.ts +0 -14
  337. package/src/forge/_generated/devManifest.json +0 -2
  338. package/src/forge/_generated/devManifest.ts +0 -62
  339. package/src/forge/_generated/envSchema.json +0 -2
  340. package/src/forge/_generated/envSchema.ts +0 -59
  341. package/src/forge/_generated/frontendGraph.json +0 -2
  342. package/src/forge/_generated/frontendGraph.ts +0 -27
  343. package/src/forge/_generated/importGuards.json +0 -2
  344. package/src/forge/_generated/importGuards.ts +0 -686
  345. package/src/forge/_generated/index.ts +0 -68
  346. package/src/forge/_generated/liveProductionManifest.json +0 -2
  347. package/src/forge/_generated/liveProductionManifest.ts +0 -23
  348. package/src/forge/_generated/liveProtocol.json +0 -2
  349. package/src/forge/_generated/liveProtocol.ts +0 -21
  350. package/src/forge/_generated/liveQueryRegistry.json +0 -2
  351. package/src/forge/_generated/liveQueryRegistry.ts +0 -9
  352. package/src/forge/_generated/liveTransportConfig.json +0 -2
  353. package/src/forge/_generated/liveTransportConfig.ts +0 -19
  354. package/src/forge/_generated/makeRegistry.json +0 -2
  355. package/src/forge/_generated/makeRegistry.ts +0 -177
  356. package/src/forge/_generated/makeTemplates.json +0 -2
  357. package/src/forge/_generated/makeTemplates.ts +0 -66
  358. package/src/forge/_generated/mockMap.json +0 -2
  359. package/src/forge/_generated/mockMap.ts +0 -7
  360. package/src/forge/_generated/operationPlaybooks.md +0 -167
  361. package/src/forge/_generated/packageGraph.json +0 -2
  362. package/src/forge/_generated/packageGraph.ts +0 -249334
  363. package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
  364. package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
  365. package/src/forge/_generated/permissionMatrix.json +0 -2
  366. package/src/forge/_generated/permissionMatrix.ts +0 -7
  367. package/src/forge/_generated/policyRegistry.json +0 -2
  368. package/src/forge/_generated/policyRegistry.ts +0 -11
  369. package/src/forge/_generated/queryRegistry.json +0 -2
  370. package/src/forge/_generated/queryRegistry.ts +0 -9
  371. package/src/forge/_generated/react.d.ts +0 -22
  372. package/src/forge/_generated/react.ts +0 -29
  373. package/src/forge/_generated/reactManifest.json +0 -2
  374. package/src/forge/_generated/reactManifest.ts +0 -19
  375. package/src/forge/_generated/rlsPolicies.json +0 -2
  376. package/src/forge/_generated/rlsPolicies.sql +0 -34
  377. package/src/forge/_generated/rlsPolicies.ts +0 -6
  378. package/src/forge/_generated/runtimeGraph.json +0 -2
  379. package/src/forge/_generated/runtimeGraph.ts +0 -8
  380. package/src/forge/_generated/runtimeMatrix.json +0 -2
  381. package/src/forge/_generated/runtimeMatrix.ts +0 -334130
  382. package/src/forge/_generated/runtimeRegistry.ts +0 -2
  383. package/src/forge/_generated/runtimeRules.md +0 -91
  384. package/src/forge/_generated/secretRegistry.json +0 -2
  385. package/src/forge/_generated/secretRegistry.ts +0 -50
  386. package/src/forge/_generated/secretsContext.ts +0 -11
  387. package/src/forge/_generated/serverApi.ts +0 -10
  388. package/src/forge/_generated/sourceMapManifest.json +0 -2
  389. package/src/forge/_generated/sourceMapManifest.ts +0 -7
  390. package/src/forge/_generated/sqlPlan.json +0 -2
  391. package/src/forge/_generated/sqlPlan.ts +0 -88
  392. package/src/forge/_generated/subscriptionManifest.json +0 -2
  393. package/src/forge/_generated/subscriptionManifest.ts +0 -7
  394. package/src/forge/_generated/symbolicationManifest.json +0 -2
  395. package/src/forge/_generated/symbolicationManifest.ts +0 -17
  396. package/src/forge/_generated/telemetryRegistry.json +0 -2
  397. package/src/forge/_generated/telemetryRegistry.ts +0 -9
  398. package/src/forge/_generated/telemetrySinks.json +0 -2
  399. package/src/forge/_generated/telemetrySinks.ts +0 -11
  400. package/src/forge/_generated/tenantScope.json +0 -2
  401. package/src/forge/_generated/tenantScope.ts +0 -8
  402. package/src/forge/_generated/testGraph.json +0 -2
  403. package/src/forge/_generated/testGraph.ts +0 -3430
  404. package/src/forge/_generated/testPlanRegistry.json +0 -2
  405. package/src/forge/_generated/testPlanRegistry.ts +0 -33
  406. package/src/forge/_generated/uiRoutes.json +0 -2
  407. package/src/forge/_generated/uiRoutes.ts +0 -16
  408. package/src/forge/_generated/uiScenarios.json +0 -2
  409. package/src/forge/_generated/uiScenarios.ts +0 -30
  410. package/src/forge/_generated/uiTestManifest.json +0 -2
  411. package/src/forge/_generated/uiTestManifest.ts +0 -27
  412. package/src/forge/_generated/workflowRegistry.json +0 -2
  413. package/src/forge/_generated/workflowRegistry.ts +0 -9
  414. package/src/forge/_generated/workflowSubscriptions.json +0 -2
  415. package/src/forge/_generated/workflowSubscriptions.ts +0 -10
@@ -1,4 +1,4 @@
1
- import { join } from "node:path";
1
+ import { dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
2
2
  import { nodeFileSystem } from "../fs/index.ts";
3
3
  import type { ApiSurface } from "../api-surface/build.ts";
4
4
  import type { ClassifiedPackage } from "../classifier/runtime-matrix.ts";
@@ -7,6 +7,7 @@ import { detectSecrets } from "../classifier/secrets.ts";
7
7
  import { GENERATOR_VERSION } from "../emitter/constants.ts";
8
8
  import { stripDeterministicHeader } from "../primitives/header.ts";
9
9
  import { canonicalJson, normalizeNewlines, serializeCanonical } from "../primitives/serialize.ts";
10
+ import { toSnakeCase } from "../data-graph/sql/naming.ts";
10
11
  import { resolveByPackageName } from "../recipes/registry.ts";
11
12
  import {
12
13
  defaultRuntimeCompatibility,
@@ -34,18 +35,30 @@ import type { QueryRegistry } from "../types/query-registry.ts";
34
35
  import type { LiveQueryRegistry } from "../types/live-query-registry.ts";
35
36
  import type { ClientManifest } from "../client-sdk/build-manifest.ts";
36
37
  import type { FrontendGraph } from "../types/frontend-graph.ts";
38
+ import type {
39
+ ForgeExternalServiceEntry,
40
+ ForgeExternalServiceGraph,
41
+ } from "../external-manifest/types.ts";
37
42
  import { createDiagnostic } from "../diagnostics/create.ts";
43
+ import { CAIR_SCHEMA_VERSION } from "../../cair/types.ts";
38
44
  import { AUTH_ENV, DEFAULT_AUTH_CLAIMS } from "../../runtime/auth/config.ts";
45
+ import {
46
+ forgeCliCommandForWorkspace,
47
+ forgeCliCommandsForWorkspace,
48
+ shouldUseLocalForgeCli,
49
+ } from "../../workspace/forge-cli.ts";
39
50
  import type {
40
51
  AgentCapabilityMap,
41
52
  AgentCapabilityMapEntry,
42
53
  AgentContract,
43
54
  AgentDependencyApiInfo,
55
+ AgentExternalEntryInfo,
44
56
  AgentFrontendRuntimeBindingInfo,
45
57
  AgentFrontendUsageInfo,
46
58
  AgentHttpEndpointInfo,
47
59
  AgentIntegrationInfo,
48
60
  AgentRuntimeRule,
61
+ AgentProtocolInfo,
49
62
  AgentToolRegistry,
50
63
  AgentPlaybook,
51
64
  } from "./types.ts";
@@ -53,6 +66,7 @@ import type {
53
66
  const AGENTS_USER_START = "<!-- user-notes:start -->";
54
67
  const AGENTS_USER_END = "<!-- user-notes:end -->";
55
68
  const DEFAULT_USER_NOTES = "Project-specific notes can go here.";
69
+ const AGENTS_USER_NOTES_TOKEN = "__FORGE_AGENTS_USER_NOTES__";
56
70
 
57
71
  export interface AgentContractInput {
58
72
  workspaceRoot: string;
@@ -74,6 +88,7 @@ export interface AgentContractInput {
74
88
  apiSurface: ApiSurface;
75
89
  clientManifest: ClientManifest;
76
90
  frontendGraph: FrontendGraph;
91
+ externalServices?: ForgeExternalServiceGraph;
77
92
  }
78
93
 
79
94
  export interface AgentContractArtifacts {
@@ -87,6 +102,7 @@ export interface AgentContractArtifacts {
87
102
  runtimeRulesMd: string;
88
103
  operationPlaybooksMd: string;
89
104
  agentQuickstartMd: string;
105
+ agentCairGuideMd: string;
90
106
  diagnostics: Diagnostic[];
91
107
  }
92
108
 
@@ -170,17 +186,188 @@ function sourceText(workspaceRoot: string, file: string | undefined): string {
170
186
  return nodeFileSystem.readText(absolute) ?? "";
171
187
  }
172
188
 
189
+ function normalizeProjectPath(path: string): string {
190
+ return path.replace(/\\/g, "/").replace(/^\.\//, "");
191
+ }
192
+
193
+ function isWorkspaceChild(workspaceRoot: string, absolutePath: string): boolean {
194
+ const relativePath = relative(resolve(workspaceRoot), absolutePath);
195
+ return relativePath === "" || (!relativePath.startsWith("..") && !isAbsolute(relativePath));
196
+ }
197
+
198
+ function localImportSpecifiers(text: string): string[] {
199
+ const specifiers: string[] = [];
200
+ const importPattern =
201
+ /\b(?:import|export)\s+(type\s+)?(?:[^"'`]*?\s+from\s+)?["'`](\.{1,2}\/[^"'`]+)["'`]/g;
202
+ for (const match of text.matchAll(importPattern)) {
203
+ if (match[1]) {
204
+ continue;
205
+ }
206
+ const specifier = match[2] ?? "";
207
+ if (specifier) {
208
+ specifiers.push(specifier);
209
+ }
210
+ }
211
+ return uniqueSorted(specifiers);
212
+ }
213
+
214
+ function resolveLocalImport(workspaceRoot: string, fromFile: string, specifier: string): string | undefined {
215
+ const basePath = normalizeProjectPath(join(dirname(fromFile), specifier));
216
+ const candidates = extname(basePath)
217
+ ? [basePath]
218
+ : [
219
+ `${basePath}.ts`,
220
+ `${basePath}.tsx`,
221
+ `${basePath}.js`,
222
+ `${basePath}.jsx`,
223
+ `${basePath}.mjs`,
224
+ `${basePath}.cjs`,
225
+ join(basePath, "index.ts"),
226
+ join(basePath, "index.tsx"),
227
+ join(basePath, "index.js"),
228
+ ];
229
+ for (const candidate of candidates.map(normalizeProjectPath)) {
230
+ if (candidate.includes("/node_modules/") || candidate.startsWith("node_modules/")) {
231
+ continue;
232
+ }
233
+ if (candidate.startsWith("src/forge/_generated/")) {
234
+ continue;
235
+ }
236
+ const absolute = resolve(workspaceRoot, candidate);
237
+ if (!isWorkspaceChild(workspaceRoot, absolute) || !nodeFileSystem.exists(absolute)) {
238
+ continue;
239
+ }
240
+ return normalizeProjectPath(relative(resolve(workspaceRoot), absolute));
241
+ }
242
+ return undefined;
243
+ }
244
+
245
+ function sourceTextWithLocalImports(
246
+ workspaceRoot: string,
247
+ file: string | undefined,
248
+ maxDepth = 2,
249
+ ): string {
250
+ if (!file) {
251
+ return "";
252
+ }
253
+ const chunks: string[] = [];
254
+ const visited = new Set<string>();
255
+
256
+ const visit = (currentFile: string, depth: number): void => {
257
+ const normalizedFile = normalizeProjectPath(currentFile);
258
+ if (visited.has(normalizedFile)) {
259
+ return;
260
+ }
261
+ visited.add(normalizedFile);
262
+ const text = sourceText(workspaceRoot, normalizedFile);
263
+ if (!text) {
264
+ return;
265
+ }
266
+ chunks.push(text);
267
+ if (depth >= maxDepth) {
268
+ return;
269
+ }
270
+ for (const specifier of localImportSpecifiers(text)) {
271
+ const imported = resolveLocalImport(workspaceRoot, normalizedFile, specifier);
272
+ if (imported) {
273
+ visit(imported, depth + 1);
274
+ }
275
+ }
276
+ };
277
+
278
+ visit(file, 0);
279
+ return chunks.join("\n");
280
+ }
281
+
282
+ function escapeRegExp(value: string): string {
283
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
284
+ }
285
+
286
+ function toSnakeCaseName(name: string): string {
287
+ return name
288
+ .replace(/([a-z0-9])([A-Z])/g, "$1_$2")
289
+ .replace(/[-\s]+/g, "_")
290
+ .toLowerCase();
291
+ }
292
+
293
+ function toCamelCaseName(name: string): string {
294
+ return name.replace(/[_-]([a-zA-Z0-9])/g, (_match, char: string) => char.toUpperCase());
295
+ }
296
+
297
+ function dbTableLookup(dataGraph: AgentContractInput["dataGraph"]): Map<string, string> {
298
+ const lookup = new Map<string, string>();
299
+ for (const table of dataGraph.tables) {
300
+ lookup.set(table.name, table.name);
301
+ lookup.set(table.exportName, table.name);
302
+ lookup.set(toSnakeCaseName(table.name), table.name);
303
+ lookup.set(toSnakeCaseName(table.exportName), table.name);
304
+ lookup.set(toCamelCaseName(table.name), table.name);
305
+ lookup.set(toCamelCaseName(table.exportName), table.name);
306
+ }
307
+ return lookup;
308
+ }
309
+
310
+ function addDbAliasesForText(
311
+ text: string,
312
+ tableLookup: Map<string, string>,
313
+ aliases: Map<string, string>,
314
+ ): void {
315
+ for (const match of text.matchAll(
316
+ /\b(?:const|let|var)\s+([A-Za-z_$][A-Za-z0-9_$]*)\s*=\s*ctx\.db(?:\.([A-Za-z_$][A-Za-z0-9_$]*)|\[\s*["'`]([^"'`]+)["'`]\s*\])/g,
317
+ )) {
318
+ const alias = match[1] ?? "";
319
+ const table = match[2] ?? match[3] ?? "";
320
+ const canonicalTable = tableLookup.get(table);
321
+ if (alias && canonicalTable) {
322
+ aliases.set(alias, canonicalTable);
323
+ }
324
+ }
325
+
326
+ for (const match of text.matchAll(/\b(?:const|let|var)\s*\{([^}]+)\}\s*=\s*ctx\.db/g)) {
327
+ const body = match[1] ?? "";
328
+ for (const part of body.split(",")) {
329
+ const [rawTable, rawAlias] = part.split(":").map((value) => value.trim());
330
+ const table = rawTable?.replace(/["'`]/g, "") ?? "";
331
+ const alias = (rawAlias ?? rawTable ?? "").replace(/\s*=.*$/, "").trim();
332
+ const canonicalTable = tableLookup.get(table);
333
+ if (canonicalTable && alias) {
334
+ aliases.set(alias, canonicalTable);
335
+ }
336
+ }
337
+ }
338
+ }
339
+
173
340
  function dbTablesForText(
174
341
  text: string,
175
- tableNames: Set<string>,
342
+ tableLookup: Map<string, string>,
176
343
  ops: Set<string>,
177
344
  ): string[] {
178
345
  const tables: string[] = [];
179
346
  for (const match of text.matchAll(/ctx\.db\.([A-Za-z_$][A-Za-z0-9_$]*)\s*\.\s*([A-Za-z_$][A-Za-z0-9_$]*)/g)) {
180
347
  const table = match[1] ?? "";
181
348
  const op = match[2] ?? "";
182
- if (tableNames.has(table) && ops.has(op)) {
183
- tables.push(table);
349
+ const canonicalTable = tableLookup.get(table);
350
+ if (canonicalTable && ops.has(op)) {
351
+ tables.push(canonicalTable);
352
+ }
353
+ }
354
+ for (const match of text.matchAll(/ctx\.db\s*\[\s*["'`]([^"'`]+)["'`]\s*\]\s*\.\s*([A-Za-z_$][A-Za-z0-9_$]*)/g)) {
355
+ const table = match[1] ?? "";
356
+ const op = match[2] ?? "";
357
+ const canonicalTable = tableLookup.get(table);
358
+ if (canonicalTable && ops.has(op)) {
359
+ tables.push(canonicalTable);
360
+ }
361
+ }
362
+ const aliases = new Map<string, string>();
363
+ addDbAliasesForText(text, tableLookup, aliases);
364
+ for (const [alias, table] of aliases) {
365
+ const aliasPattern = new RegExp(`\\b${escapeRegExp(alias)}\\s*\\.\\s*([A-Za-z_$][A-Za-z0-9_$]*)`, "g");
366
+ for (const match of text.matchAll(aliasPattern)) {
367
+ const op = match[1] ?? "";
368
+ if (ops.has(op)) {
369
+ tables.push(table);
370
+ }
184
371
  }
185
372
  }
186
373
  return uniqueSorted(tables);
@@ -189,14 +376,14 @@ function dbTablesForText(
189
376
  function dbTablesForFile(
190
377
  workspaceRoot: string,
191
378
  file: string | undefined,
192
- tableNames: Set<string>,
379
+ tableLookup: Map<string, string>,
193
380
  ops: Set<string>,
194
381
  ): string[] {
195
- return dbTablesForText(sourceText(workspaceRoot, file), tableNames, ops);
382
+ return dbTablesForText(sourceTextWithLocalImports(workspaceRoot, file), tableLookup, ops);
196
383
  }
197
384
 
198
385
  function emittedEventsForFile(workspaceRoot: string, file: string | undefined): string[] {
199
- const text = sourceText(workspaceRoot, file);
386
+ const text = sourceTextWithLocalImports(workspaceRoot, file);
200
387
  return uniqueSorted(
201
388
  [...text.matchAll(/ctx\.emit\s*\(\s*["'`]([^"'`]+)["'`]/g)]
202
389
  .map((match) => match[1] ?? ""),
@@ -248,7 +435,7 @@ function runtimeRules(): AgentRuntimeRule[] {
248
435
  {
249
436
  context: "command",
250
437
  allowed: ["ctx.db writes", "ctx.emit", "ctx.telemetry buffered events"],
251
- forbidden: ["network packages", "ctx.secrets", "ctx.ai", "ctx.ai.runAgent", "ctx.agent.run", "process.env", "filesystem access"],
438
+ forbidden: ["network packages", "ctx.secrets", "ctx.ai", "ctx.ai.runAgent", "ctx.agent.run", "direct secret/env access", "filesystem access"],
252
439
  },
253
440
  {
254
441
  context: "query",
@@ -273,6 +460,104 @@ function runtimeRules(): AgentRuntimeRule[] {
273
460
  ];
274
461
  }
275
462
 
463
+ type TenantTableInfo = {
464
+ tenantIdColumn: string;
465
+ tenantField: string;
466
+ };
467
+
468
+ function buildTenantTableLookup(tenantScope: TenantScope, dataGraph: DataGraph): Map<string, TenantTableInfo> {
469
+ const lookup = new Map<string, TenantTableInfo>();
470
+ for (const scoped of tenantScope.tables) {
471
+ const table = dataGraph.tables.find((candidate) =>
472
+ candidate.name === scoped.table ||
473
+ candidate.exportName === scoped.exportName ||
474
+ toSnakeCase(candidate.name) === scoped.table ||
475
+ toSnakeCase(candidate.exportName) === scoped.table
476
+ );
477
+ const tenantField = table?.fields.find((field) => toSnakeCase(field.name) === scoped.tenantIdColumn)?.name ??
478
+ scoped.tenantIdColumn;
479
+ const info = { tenantIdColumn: scoped.tenantIdColumn, tenantField };
480
+ for (const key of uniqueSorted([
481
+ scoped.table,
482
+ scoped.exportName,
483
+ table?.name,
484
+ table?.exportName,
485
+ ].filter((key): key is string => typeof key === "string" && key.length > 0))) {
486
+ lookup.set(key, info);
487
+ }
488
+ }
489
+ return lookup;
490
+ }
491
+
492
+ function agentProtocols(workspaceRoot: string): AgentProtocolInfo[] {
493
+ return [
494
+ {
495
+ id: "cair",
496
+ kind: "agent-protocol",
497
+ version: CAIR_SCHEMA_VERSION,
498
+ guide: "src/forge/_generated/agentCairGuide.md",
499
+ commands: forgeCliCommandsForWorkspace(workspaceRoot, [
500
+ "forge cair snapshot",
501
+ "forge cair query \"Q ST\"",
502
+ "forge cair query \"Q S name=<symbol>\"",
503
+ "forge cair query \"Q D S#1\"",
504
+ "forge cair query \"Q R S#1\"",
505
+ "forge cair query \"Q I S#1\"",
506
+ "forge cair action --plan \"A RN t=S#1 nn=<newName>\"",
507
+ "forge cair action \"A APPLY plan=<P#|path>\"",
508
+ "forge cair action \"A ROLLBACK journal=<path>\"",
509
+ ]),
510
+ preferredFor: [
511
+ "compact repository orientation",
512
+ "symbol lookup before file reads",
513
+ "semantic code edits",
514
+ "guarded refactors",
515
+ "Forge-native feature creation",
516
+ "impact-aware test selection",
517
+ "token-efficient programming",
518
+ ],
519
+ readQueries: [
520
+ "Q ST",
521
+ "Q S name=<symbol>",
522
+ "Q D S#1",
523
+ "Q R S#1",
524
+ "Q I S#1",
525
+ "Q T S#1",
526
+ "Q DEP.API package=<pkg> symbol=<export>",
527
+ ],
528
+ mutationActions: [
529
+ "A RN t=S#1 nn=<newName>",
530
+ "A MV t=S#1 to=<path>",
531
+ "A OI f=M#1",
532
+ "A FMT f=M#1",
533
+ "A MC n=<command>",
534
+ "A MQ n=<query>",
535
+ "A MA n=<action>",
536
+ "A MT n=<table> fields=<fields>",
537
+ "A AT t=S#1 kind=unit",
538
+ "A WX t=S#1 file=src/index.ts",
539
+ "A APPLY plan=<P#|path>",
540
+ "A ROLLBACK journal=<path>",
541
+ ],
542
+ compactAliases: [
543
+ "Q ST=Q STATUS",
544
+ "Q S=Q SYMBOL",
545
+ "Q D=Q DEF",
546
+ "Q R=Q REFS",
547
+ "Q I=Q IMPACT",
548
+ "A RN=A RENAME.SYMBOL",
549
+ "A MV=A MOVE.SYMBOL",
550
+ "A OI=A ORGANIZE.IMPORTS",
551
+ "A FMT=A FORMAT",
552
+ "A MC=A MAKE.COMMAND",
553
+ "A MT=A MAKE.TABLE",
554
+ "A AT=A ADD.TEST",
555
+ "A WX=A WIRE.EXPORT",
556
+ ],
557
+ },
558
+ ];
559
+ }
560
+
276
561
  function playbooks(): AgentPlaybook[] {
277
562
  return [
278
563
  {
@@ -341,7 +626,7 @@ function playbooks(): AgentPlaybook[] {
341
626
  "Prefer AI SDK ToolLoopAgent semantics through ctx.agent.run or ctx.ai.runAgent instead of custom loops.",
342
627
  "Use stopWhen with stepCount or terminal tool calls to prevent unbounded loops.",
343
628
  "Run agents only in actions, workflows, endpoints, or server code.",
344
- "Run forge inspect all --json and confirm agentContract.ai.agents lists the agent.",
629
+ "Run forge inspect ai --json or forge agent print-context --json and confirm the generated context lists the agent.",
345
630
  "Use forge ai trace <traceId> --json to inspect agent runs and tool calls.",
346
631
  ],
347
632
  },
@@ -358,7 +643,7 @@ function playbooks(): AgentPlaybook[] {
358
643
  {
359
644
  title: "Scaffold a resource",
360
645
  steps: [
361
- "Run forge make resource <name> --fields name:type,status:enum(open,closed) --dry-run --json.",
646
+ "Run forge make resource <name> --fields name:type,status:enum=open+closed --dry-run --json.",
362
647
  "Review the plan and diagnostics.",
363
648
  "Run forge make resource <name> --fields name:type --with-ui --yes when the resource should be visible in the web app.",
364
649
  "Run forge generate.",
@@ -447,17 +732,17 @@ function playbooks(): AgentPlaybook[] {
447
732
  "Run forge dev --once --json for a one-shot diagnostic cycle.",
448
733
  "Use --api-only, --web-only, --no-watch, or --no-worker only when narrowing the loop intentionally.",
449
734
  "When a web app exists, forge dev starts the API runtime and the web dev server together and prints both URLs.",
450
- "Use generated client and React hooks through web/lib/forge.ts.",
735
+ "Use generated client bindings through web/lib/forge.ts, web/src/lib/forge.ts, or Nuxt web/composables/forge.ts.",
451
736
  ],
452
737
  },
453
738
  {
454
739
  title: "Add or update frontend",
455
740
  steps: [
456
- "Run forge make ui --framework vite --dry-run --json when the app does not have a web root.",
741
+ "Run forge make ui --framework vite --dry-run --json or forge make ui --framework nuxt --dry-run --json when the app does not have a web root.",
457
742
  "Run forge make ai-chat support --dry-run --json to add a chat surface backed by /ai/agents/chat streaming and /ai/agents/run JSON automation.",
458
- "Use web/lib/forge.ts as the generated client bridge.",
459
- "Mount ForgeProvider once in the web app provider/layout layer; use devAuth for local development.",
460
- "Use useQuery, useCommand, and useLiveQuery instead of raw /commands or /queries fetches.",
743
+ "Use web/lib/forge.ts, web/src/lib/forge.ts, or web/composables/forge.ts as the generated client bridge.",
744
+ "Mount ForgeProvider or install the Nuxt Forge plugin once in the web app provider/layout layer; use devAuth for local development.",
745
+ "Use useQuery/useCommand/useLiveQuery or useForgeQuery/useForgeCommand/useForgeLiveQuery instead of raw /commands or /queries fetches.",
461
746
  "Run forge generate so frontendGraph and agentContract include routes and bindings.",
462
747
  "Run forge inspect capabilities --json to confirm UI actions map to runtime capabilities.",
463
748
  "Run forge dev --once --json and forge doctor --json.",
@@ -536,7 +821,11 @@ function runtimeEntriesWithoutFrontend(contract: AgentContract): AgentCapability
536
821
  emits: commandEntry.emits,
537
822
  dependencies: [],
538
823
  },
539
- notes: ["Runtime entry is available to agents even though no frontend usage was detected."],
824
+ notes: [
825
+ commandEntry.source === "external"
826
+ ? "External runtime entry is imported from a Forge manifest; execution requires an external runtime bridge."
827
+ : "Runtime entry is available to agents even though no frontend usage was detected.",
828
+ ],
540
829
  });
541
830
  }
542
831
  }
@@ -557,7 +846,11 @@ function runtimeEntriesWithoutFrontend(contract: AgentContract): AgentCapability
557
846
  emits: [],
558
847
  dependencies: [],
559
848
  },
560
- notes: ["Runtime entry is available to agents even though no frontend usage was detected."],
849
+ notes: [
850
+ queryEntry.source === "external"
851
+ ? "External runtime entry is imported from a Forge manifest; execution requires an external runtime bridge."
852
+ : "Runtime entry is available to agents even though no frontend usage was detected.",
853
+ ],
561
854
  });
562
855
  }
563
856
  }
@@ -703,9 +996,15 @@ function buildAgentToolRegistry(contract: AgentContract): AgentToolRegistry {
703
996
  dependencies: [],
704
997
  readOnly: false,
705
998
  risk: "write" as const,
706
- needsApproval: true,
999
+ needsApproval: command.source === "external"
1000
+ ? command.external?.needsApproval ?? (command.external?.risk !== "read")
1001
+ : true,
707
1002
  requiresAuth: command.policy !== undefined && command.policy !== "public",
708
- execution: "forge-runtime-endpoint" as const,
1003
+ ...(command.source ? { source: command.source } : {}),
1004
+ ...(command.external ? { external: command.external } : {}),
1005
+ execution: command.source === "external"
1006
+ ? "external-runtime-endpoint" as const
1007
+ : "forge-runtime-endpoint" as const,
709
1008
  })),
710
1009
  ...contract.queries.map((query) => ({
711
1010
  name: autoToolName("query", query.name),
@@ -723,7 +1022,11 @@ function buildAgentToolRegistry(contract: AgentContract): AgentToolRegistry {
723
1022
  risk: "read" as const,
724
1023
  needsApproval: false,
725
1024
  requiresAuth: query.policy !== undefined && query.policy !== "public",
726
- execution: "forge-runtime-endpoint" as const,
1025
+ ...(query.source ? { source: query.source } : {}),
1026
+ ...(query.external ? { external: query.external } : {}),
1027
+ execution: query.source === "external"
1028
+ ? "external-runtime-endpoint" as const
1029
+ : "forge-runtime-endpoint" as const,
727
1030
  })),
728
1031
  ...contract.liveQueries.map((liveQuery) => ({
729
1032
  name: autoToolName("liveQuery", liveQuery.name),
@@ -741,6 +1044,7 @@ function buildAgentToolRegistry(contract: AgentContract): AgentToolRegistry {
741
1044
  risk: "read" as const,
742
1045
  needsApproval: false,
743
1046
  requiresAuth: liveQuery.policy !== undefined && liveQuery.policy !== "public",
1047
+ source: "local" as const,
744
1048
  execution: "forge-runtime-endpoint" as const,
745
1049
  })),
746
1050
  ].sort((a, b) => a.name.localeCompare(b.name));
@@ -794,6 +1098,39 @@ function httpEndpointFor(
794
1098
  };
795
1099
  }
796
1100
 
1101
+ function externalEndpointFor(entry: ForgeExternalServiceEntry): AgentHttpEndpointInfo {
1102
+ const encodedService = encodeURIComponent(entry.service);
1103
+ const encodedName = encodeURIComponent(entry.name);
1104
+ const collection = entry.kind === "query" ? "queries" : "commands";
1105
+ return {
1106
+ method: entry.method ?? "POST",
1107
+ path: entry.path ?? `/external/${encodedService}/${collection}/${encodedName}`,
1108
+ exampleBody: { args: {} },
1109
+ };
1110
+ }
1111
+
1112
+ function externalEntryInfo(entry: ForgeExternalServiceEntry): AgentExternalEntryInfo {
1113
+ return {
1114
+ service: entry.service,
1115
+ language: entry.language,
1116
+ ...(entry.framework ? { framework: entry.framework } : {}),
1117
+ transport: entry.transport,
1118
+ ...(entry.transaction ? { transaction: entry.transaction } : {}),
1119
+ ...(entry.risk ? { risk: entry.risk } : {}),
1120
+ ...(typeof entry.needsApproval === "boolean" ? { needsApproval: entry.needsApproval } : {}),
1121
+ effects: entry.effects ?? [],
1122
+ ...(entry.description ? { description: entry.description } : {}),
1123
+ };
1124
+ }
1125
+
1126
+ function externalFrontendUsage(entry: ForgeExternalServiceEntry): AgentFrontendUsageInfo {
1127
+ return {
1128
+ hook: `external manifest '${entry.service}.${entry.name}'; runtime bridge required before client invocation`,
1129
+ routes: [],
1130
+ components: [],
1131
+ };
1132
+ }
1133
+
797
1134
  function frontendUsageFor(
798
1135
  frontendGraph: FrontendGraph,
799
1136
  kind: "command" | "query" | "liveQuery" | "action",
@@ -901,9 +1238,7 @@ export function buildAgentContractArtifacts(
901
1238
  input: AgentContractInput,
902
1239
  ): AgentContractArtifacts {
903
1240
  const project = readPackageInfo(input.workspaceRoot);
904
- const tenantTables = new Map(
905
- input.tenantScope.tables.map((table) => [table.table, table.tenantIdColumn]),
906
- );
1241
+ const tenantTables = buildTenantTableLookup(input.tenantScope, input.dataGraph);
907
1242
  const commandAuth = new Map(
908
1243
  input.policyRegistry.commandAuth.map((binding) => [binding.commandName, binding.auth]),
909
1244
  );
@@ -915,16 +1250,18 @@ export function buildAgentContractArtifacts(
915
1250
  );
916
1251
 
917
1252
  const runtimeEntries = new Map(input.runtimeGraph.entries.map((entry) => [entry.name, entry]));
918
- const tableNames = new Set(input.dataGraph.tables.map((table) => table.name));
919
- const commandInfos: AgentContract["commands"] = sorted(Object.keys(input.apiSurface.commands), (name) => name).map((name) => {
1253
+ const tableLookup = dbTableLookup(input.dataGraph);
1254
+ const externalEntries = input.externalServices?.services.flatMap((service) => service.entries) ?? [];
1255
+ const localCommandInfos: AgentContract["commands"] = sorted(Object.keys(input.apiSurface.commands), (name) => name).map((name) => {
920
1256
  const entry = runtimeEntries.get(name);
921
1257
  const file = entry?.file ?? "";
922
1258
  return {
923
1259
  name,
924
1260
  file,
1261
+ source: "local" as const,
925
1262
  policy: authPolicy(commandAuth.get(name)),
926
- tablesRead: dbTablesForFile(input.workspaceRoot, file, tableNames, DB_READ_OPS),
927
- tablesWritten: dbTablesForFile(input.workspaceRoot, file, tableNames, DB_WRITE_OPS),
1263
+ tablesRead: dbTablesForFile(input.workspaceRoot, file, tableLookup, DB_READ_OPS),
1264
+ tablesWritten: dbTablesForFile(input.workspaceRoot, file, tableLookup, DB_WRITE_OPS),
928
1265
  emits: emittedEventsForFile(input.workspaceRoot, file),
929
1266
  allowedPackages: entry ? packageNamesForModule(input.appGraph, entry.moduleId) : [],
930
1267
  forbiddenCapabilities: forbiddenForContext(input.classified, "command"),
@@ -932,21 +1269,62 @@ export function buildAgentContractArtifacts(
932
1269
  frontend: frontendUsageFor(input.frontendGraph, "command", name),
933
1270
  };
934
1271
  });
935
- const queryInfos: AgentContract["queries"] = sorted(input.queryRegistry.queries, (query) => query.name).map((query) => ({
1272
+ const externalCommandInfos: AgentContract["commands"] = externalEntries
1273
+ .filter((entry) => entry.kind === "command")
1274
+ .map((entry) => ({
1275
+ name: `${entry.service}.${entry.name}`,
1276
+ file: `external:${entry.service}`,
1277
+ source: "external" as const,
1278
+ external: externalEntryInfo(entry),
1279
+ ...(entry.policy ? { policy: entry.policy } : {}),
1280
+ tablesRead: [],
1281
+ tablesWritten: entry.transaction === "read-only" ? [] : [`external:${entry.service}`],
1282
+ emits: entry.effects ?? [],
1283
+ allowedPackages: [],
1284
+ forbiddenCapabilities: [],
1285
+ http: externalEndpointFor(entry),
1286
+ frontend: externalFrontendUsage(entry),
1287
+ }));
1288
+ const commandInfos: AgentContract["commands"] = sorted(
1289
+ [...localCommandInfos, ...externalCommandInfos],
1290
+ (command) => command.name,
1291
+ );
1292
+ const localQueryInfos: AgentContract["queries"] = sorted(input.queryRegistry.queries, (query) => query.name).map((query) => ({
936
1293
  name: query.name,
937
1294
  file: query.file,
1295
+ source: "local" as const,
938
1296
  policy: authPolicy(queryAuth.get(query.name)),
939
1297
  readOnly: true,
940
1298
  tenantScoped: input.tenantScope.tables.length > 0,
941
- tablesRead: dbTablesForFile(input.workspaceRoot, query.file, tableNames, DB_READ_OPS),
1299
+ tablesRead: dbTablesForFile(input.workspaceRoot, query.file, tableLookup, DB_READ_OPS),
942
1300
  allowedPackages: packageNamesForModule(input.appGraph, query.moduleId),
943
1301
  forbiddenCapabilities: forbiddenForContext(input.classified, "query"),
944
1302
  http: httpEndpointFor("query", query.name),
945
1303
  frontend: frontendUsageFor(input.frontendGraph, "query", query.name),
946
1304
  }));
1305
+ const externalQueryInfos: AgentContract["queries"] = externalEntries
1306
+ .filter((entry) => entry.kind === "query")
1307
+ .map((entry) => ({
1308
+ name: `${entry.service}.${entry.name}`,
1309
+ file: `external:${entry.service}`,
1310
+ source: "external" as const,
1311
+ external: externalEntryInfo(entry),
1312
+ ...(entry.policy ? { policy: entry.policy } : {}),
1313
+ readOnly: true as const,
1314
+ tenantScoped: entry.tenantScoped ?? false,
1315
+ tablesRead: [`external:${entry.service}`],
1316
+ allowedPackages: [],
1317
+ forbiddenCapabilities: [],
1318
+ http: externalEndpointFor(entry),
1319
+ frontend: externalFrontendUsage(entry),
1320
+ }));
1321
+ const queryInfos: AgentContract["queries"] = sorted(
1322
+ [...localQueryInfos, ...externalQueryInfos],
1323
+ (query) => query.name,
1324
+ );
947
1325
  const liveQueryInfos: AgentContract["liveQueries"] = sorted(input.liveQueryRegistry.liveQueries, (liveQuery) => liveQuery.name).map(
948
1326
  (liveQuery) => {
949
- const tablesRead = dbTablesForFile(input.workspaceRoot, liveQuery.file, tableNames, DB_READ_OPS);
1327
+ const tablesRead = dbTablesForFile(input.workspaceRoot, liveQuery.file, tableLookup, DB_READ_OPS);
950
1328
  return {
951
1329
  name: liveQuery.name,
952
1330
  file: liveQuery.file,
@@ -968,6 +1346,7 @@ export function buildAgentContractArtifacts(
968
1346
  queries: queryInfos,
969
1347
  liveQueries: liveQueryInfos,
970
1348
  });
1349
+ const protocols = agentProtocols(input.workspaceRoot);
971
1350
  const contract: AgentContract = {
972
1351
  schemaVersion: "0.1.0",
973
1352
  generatorVersion: GENERATOR_VERSION,
@@ -1003,18 +1382,22 @@ export function buildAgentContractArtifacts(
1003
1382
  }),
1004
1383
  ),
1005
1384
  data: {
1006
- tables: sorted(input.dataGraph.tables, (table) => table.name).map((table) => ({
1007
- name: table.name,
1008
- file: table.file,
1009
- tenantScoped: tenantTables.has(table.name),
1010
- ...(tenantTables.has(table.name) ? { tenantField: tenantTables.get(table.name) } : {}),
1011
- fields: uniqueSorted(table.fields.map((field) => field.name)),
1012
- })),
1385
+ tables: sorted(input.dataGraph.tables, (table) => table.name).map((table) => {
1386
+ const tenantInfo = tenantTables.get(table.name);
1387
+ return {
1388
+ name: table.name,
1389
+ file: table.file,
1390
+ tenantScoped: Boolean(tenantInfo),
1391
+ ...(tenantInfo ? { tenantField: tenantInfo.tenantField } : {}),
1392
+ fields: uniqueSorted(table.fields.map((field) => field.name)),
1393
+ };
1394
+ }),
1013
1395
  },
1014
1396
  policies: sorted(input.policyRegistry.policies, (policy) => policy.name).map((policy) => ({
1015
1397
  name: policy.name,
1016
1398
  kind: policy.kind,
1017
1399
  roles: uniqueSorted(policy.roles),
1400
+ permissions: uniqueSorted(policy.permissions ?? []),
1018
1401
  file: policy.file,
1019
1402
  })),
1020
1403
  packages: sorted(input.packageGraph.packages, (pkg) => pkg.name).map((pkg) => {
@@ -1028,6 +1411,19 @@ export function buildAgentContractArtifacts(
1028
1411
  }),
1029
1412
  dependencyApis: buildDependencyApis(input.packageGraph),
1030
1413
  integrations: buildIntegrations(input.classified),
1414
+ externalServices: (input.externalServices?.services ?? []).map((service) => ({
1415
+ name: service.name,
1416
+ language: service.language,
1417
+ ...(service.framework ? { framework: service.framework } : {}),
1418
+ transport: service.transport,
1419
+ ...(service.baseUrl ? { baseUrl: service.baseUrl } : {}),
1420
+ ...(service.command ? { command: service.command } : {}),
1421
+ ...(service.health ? { health: service.health } : {}),
1422
+ commands: sorted(service.entries.filter((entry) => entry.kind === "command"), (entry) => entry.name)
1423
+ .map((entry) => `${service.name}.${entry.name}`),
1424
+ queries: sorted(service.entries.filter((entry) => entry.kind === "query"), (entry) => entry.name)
1425
+ .map((entry) => `${service.name}.${entry.name}`),
1426
+ })),
1031
1427
  secrets: sorted(input.secretRegistry.secrets, (secret) => secret.name).map((secret) => ({
1032
1428
  name: secret.name,
1033
1429
  integration: secret.integration,
@@ -1141,10 +1537,11 @@ export function buildAgentContractArtifacts(
1141
1537
  },
1142
1538
  rules: runtimeRules(),
1143
1539
  playbooks: playbooks(),
1540
+ agentProtocols: protocols,
1144
1541
  commandsToRun: {
1145
- beforeEditing: ["forge do inspect --json", "forge dev --once --json", "forge inspect all --json", "forge check --json"],
1146
- afterEditing: ["forge generate", "forge check", "forge verify --standard", "forge verify --strict"],
1147
- dev: ["forge dev", "forge dev --once --json", "forge do fix --json", "forge do verify --json", "forge dev --api-only", "forge dev --web-only"],
1542
+ beforeEditing: forgeCliCommandsForWorkspace(input.workspaceRoot, ["forge agent onboard --target codex --json", "forge status --json", "forge changed --json", "forge handoff --json", "forge do inspect --json", "forge cair snapshot", "forge cair query \"Q ST\"", "forge dev --once --json", "forge agent print-context --json", "forge check --json"]),
1543
+ afterEditing: forgeCliCommandsForWorkspace(input.workspaceRoot, ["forge generate", "forge check", "forge verify --standard", finalVerifyCommand(input.workspaceRoot)]),
1544
+ dev: forgeCliCommandsForWorkspace(input.workspaceRoot, ["forge dev", "forge dev --once --json", "forge handoff --json", "forge do fix --json", "forge do verify --json", "forge dev --api-only", "forge dev --web-only"]),
1148
1545
  },
1149
1546
  };
1150
1547
 
@@ -1153,7 +1550,7 @@ export function buildAgentContractArtifacts(
1153
1550
  ? (nodeFileSystem.readText(existingAgentsPath) ?? "")
1154
1551
  : null;
1155
1552
  const userNotes = extractUserNotes(existingAgents);
1156
- const agentsMd = renderAgentsMd(contract, userNotes);
1553
+ const agentsMd = renderAgentsMd(contract, userNotes, input.workspaceRoot);
1157
1554
  const toolRegistry = buildAgentToolRegistry(contract);
1158
1555
  const capabilityMap = buildCapabilityMap(contract);
1159
1556
  const capabilityMapMd = renderCapabilityMapMd(capabilityMap);
@@ -1161,7 +1558,8 @@ export function buildAgentContractArtifacts(
1161
1558
  const appMapMd = renderAppMapMd(contract);
1162
1559
  const runtimeRulesMd = renderRuntimeRulesMd(contract.rules);
1163
1560
  const operationPlaybooksMd = renderOperationPlaybooksMd(contract.playbooks);
1164
- const agentQuickstartMd = renderAgentQuickstartMd();
1561
+ const agentQuickstartMd = renderAgentQuickstartMd(input.workspaceRoot);
1562
+ const agentCairGuideMd = renderAgentCairGuideMd(contract, input.workspaceRoot);
1165
1563
  const diagnostics = scanAgentContractForLeaks(contract, [
1166
1564
  agentsMd,
1167
1565
  agentToolsMd,
@@ -1170,6 +1568,7 @@ export function buildAgentContractArtifacts(
1170
1568
  runtimeRulesMd,
1171
1569
  operationPlaybooksMd,
1172
1570
  agentQuickstartMd,
1571
+ agentCairGuideMd,
1173
1572
  ]);
1174
1573
 
1175
1574
  return {
@@ -1183,6 +1582,7 @@ export function buildAgentContractArtifacts(
1183
1582
  runtimeRulesMd,
1184
1583
  operationPlaybooksMd,
1185
1584
  agentQuickstartMd,
1585
+ agentCairGuideMd,
1186
1586
  diagnostics: [...diagnostics, ...capabilityMap.diagnostics],
1187
1587
  };
1188
1588
  }
@@ -1229,7 +1629,22 @@ export function serializeAgentToolRegistryTs(registry: AgentToolRegistry): strin
1229
1629
  return `export const agentTools = ${JSON.stringify(parsed, null, 2)} as const;\n`;
1230
1630
  }
1231
1631
 
1232
- function renderAgentsMd(contract: AgentContract, userNotes: string): string {
1632
+ function localizeForgeCliMarkdown(workspaceRoot: string, markdown: string): string {
1633
+ return shouldUseLocalForgeCli(workspaceRoot)
1634
+ ? markdown.replace(/\bforge (?=[a-z])/g, "node bin/forge.mjs ")
1635
+ : markdown;
1636
+ }
1637
+
1638
+ function finalVerifyCommand(workspaceRoot: string): string {
1639
+ const command = shouldUseLocalForgeCli(workspaceRoot) ? "forge verify framework" : "forge verify --strict";
1640
+ return forgeCliCommandForWorkspace(workspaceRoot, command);
1641
+ }
1642
+
1643
+ function renderAgentsMd(contract: AgentContract, userNotes: string, workspaceRoot: string): string {
1644
+ const finalVerify = finalVerifyCommand(workspaceRoot);
1645
+ const cliEntrypoint = shouldUseLocalForgeCli(workspaceRoot)
1646
+ ? "This is the ForgeOS framework checkout. Use `node bin/forge.mjs ...` so maintainer commands run against this source tree; reserve the global `forge` command for installed-package smoke tests."
1647
+ : "Use the installed `forge` command for app workflows.";
1233
1648
  const tenantTables = contract.data.tables
1234
1649
  .filter((table) => table.tenantScoped)
1235
1650
  .map((table) => `${table.name} via ${table.tenantField}`);
@@ -1244,7 +1659,7 @@ function renderAgentsMd(contract: AgentContract, userNotes: string): string {
1244
1659
  `${agent.name}: ${agent.provider}/${agent.model} with ${agent.tools.length > 0 ? agent.tools.join(", ") : "no tools"}`,
1245
1660
  );
1246
1661
 
1247
- return normalizeNewlines(`# AGENTS.md
1662
+ const generated = localizeForgeCliMarkdown(workspaceRoot, `# AGENTS.md
1248
1663
 
1249
1664
  <!-- forge-generated:start -->
1250
1665
 
@@ -1252,23 +1667,51 @@ function renderAgentsMd(contract: AgentContract, userNotes: string): string {
1252
1667
 
1253
1668
  This is a ForgeOS application named \`${contract.project.name}\`.
1254
1669
 
1670
+ ## CLI entrypoint
1671
+
1672
+ ${cliEntrypoint}
1673
+
1255
1674
  ## Required workflow
1256
1675
 
1257
1676
  Before editing:
1258
1677
 
1259
1678
  \`\`\`bash
1679
+ forge agent onboard --target codex --json
1680
+ forge status --json
1681
+ forge changed --json
1682
+ forge handoff --json
1260
1683
  forge do inspect --json
1261
1684
  forge dev --once --json
1262
- forge inspect all --json
1685
+ forge agent print-context --json
1263
1686
  forge check --json
1264
1687
  \`\`\`
1265
1688
 
1689
+ ## CAIR first
1690
+
1691
+ Before reading large files or hand-writing patches, prefer the generated CAIR guide:
1692
+
1693
+ \`\`\`bash
1694
+ forge cair snapshot
1695
+ forge cair query "Q ST"
1696
+ forge cair query "Q S name=<symbol>"
1697
+ forge cair query "Q D S#1"
1698
+ forge cair query "Q R S#1"
1699
+ forge cair query "Q I S#1"
1700
+ \`\`\`
1701
+
1702
+ Use \`src/forge/_generated/agentCairGuide.md\` for the full compact protocol. Plan CAIR mutations before applying them:
1703
+
1704
+ \`\`\`bash
1705
+ forge cair action --plan "A RN t=S#1 nn=<newName>"
1706
+ forge cair action "A APPLY plan=<returned-plan-path>"
1707
+ \`\`\`
1708
+
1266
1709
  After editing:
1267
1710
 
1268
1711
  \`\`\`bash
1269
1712
  forge generate
1270
1713
  forge check
1271
- forge verify --strict
1714
+ ${finalVerify}
1272
1715
  \`\`\`
1273
1716
 
1274
1717
  ## Do not edit
@@ -1296,7 +1739,7 @@ Template apps may ignore \`src/forge/_generated/**\` and \`forge.lock\` in git t
1296
1739
  ## Runtime rules
1297
1740
 
1298
1741
  - Do not import network packages inside \`command\`, \`query\`, or \`liveQuery\`.
1299
- - Do not use \`process.env\` directly.
1742
+ - Do not read secrets or server runtime config through \`process.env\` in Forge runtime code; use \`ctx.secrets\` or generated config context. Public frontend bridge env such as \`NEXT_PUBLIC_*\` and \`NUXT_PUBLIC_*\` is allowed in web bridge files.
1300
1743
  - Do not access cross-tenant data.
1301
1744
  - Commands must use \`ctx.emit\` for side effects.
1302
1745
  - Actions and workflows handle side effects after commit.
@@ -1310,8 +1753,10 @@ forge do fix --json
1310
1753
  forge do verify --json
1311
1754
  forge dev --once --json
1312
1755
  forge dev
1756
+ forge handoff --json
1313
1757
  forge inspect app --json
1314
1758
  forge inspect all --json
1759
+ forge inspect all --full --json
1315
1760
  forge inspect frontend --json
1316
1761
  forge inspect capabilities --json
1317
1762
  forge inspect agent-tools --json
@@ -1328,12 +1773,13 @@ forge doctor
1328
1773
  forge doctor windows --json
1329
1774
  forge setup windows --json
1330
1775
  forge agent print-context --json
1776
+ forge agent doctor --target codex --json
1331
1777
  forge ai tools --json
1332
1778
  forge ai agents --json
1333
1779
  forge ai trace <traceId> --json
1334
1780
  forge verify --smoke
1335
1781
  forge verify --standard
1336
- forge verify --strict
1782
+ ${finalVerify}
1337
1783
  \`\`\`
1338
1784
 
1339
1785
  ## Data
@@ -1388,9 +1834,9 @@ ${contract.frontend.dev ? `- Web URL: ${contract.frontend.dev.url}
1388
1834
 
1389
1835
  Rules:
1390
1836
 
1391
- - Use the local \`web/**/lib/forge.ts\` bridge to generated hooks.
1392
- - Mount \`<ForgeProvider devAuth>\` in local development.
1393
- - Use \`useQuery\`, \`useCommand\`, and \`useLiveQuery\` instead of raw Forge endpoint fetches in React components.
1837
+ - Use the local \`web/**/lib/forge.ts\` or Nuxt \`web/composables/forge.ts\` bridge to generated bindings.
1838
+ - Mount \`<ForgeProvider devAuth>\` or install the Nuxt Forge plugin in local development.
1839
+ - Use \`useQuery\`/\`useCommand\`/\`useLiveQuery\` or \`useForgeQuery\`/\`useForgeCommand\`/\`useForgeLiveQuery\` instead of raw Forge endpoint fetches in components.
1394
1840
  - Keep frontend routes reflected in \`src/forge/_generated/frontendGraph.json\`.
1395
1841
 
1396
1842
  ## Common tasks
@@ -1413,16 +1859,17 @@ forge do verify --json
1413
1859
  1. Add file in \`src/commands\`.
1414
1860
  2. Declare \`auth: can("...")\`.
1415
1861
  3. Run \`forge generate\`.
1416
- 4. Run \`forge verify --strict\`.
1862
+ 4. Run \`${finalVerify}\`.
1417
1863
 
1418
1864
  ### Scaffold a resource
1419
1865
 
1420
1866
  Use:
1421
1867
 
1422
1868
  \`\`\`bash
1423
- forge make resource <name> --fields title:text,status:enum(open,closed) --dry-run --json
1424
- forge make resource <name> --fields title:text,status:enum(open,closed) --with-ui --yes
1869
+ forge make resource <name> --fields title:text,status:enum=open+closed --dry-run --json
1870
+ forge make resource <name> --fields title:text,status:enum=open+closed --with-ui --yes
1425
1871
  forge make ui --framework vite --dry-run --json
1872
+ forge make ui --framework nuxt --dry-run --json
1426
1873
  forge make ai-chat support --dry-run --json
1427
1874
  \`\`\`
1428
1875
 
@@ -1439,7 +1886,7 @@ forge inspect frontend --json
1439
1886
  forge inspect capabilities --json
1440
1887
  \`\`\`
1441
1888
 
1442
- \`forge dev\` starts the API runtime and web app together when \`web/\` exists. \`forge dev --once --json\` reports routes, components, \`ForgeProvider\`, bridge files, generated client bindings, direct runtime fetch warnings, capability-map parity warnings, and fix hints.
1889
+ \`forge dev\` starts the API runtime and web app together when \`web/\` exists. \`forge dev --once --json\` reports routes, components, providers/plugins, bridge files, generated client bindings, direct runtime fetch warnings, capability-map parity warnings, and fix hints.
1443
1890
 
1444
1891
  ### Apply a feature blueprint
1445
1892
 
@@ -1479,7 +1926,7 @@ forge test run --changed --timeout-ms 120000 --json
1479
1926
  forge verify --standard
1480
1927
  \`\`\`
1481
1928
 
1482
- Use \`forge verify --standard\` for the normal agent development loop. Finish handoffs with \`forge verify --strict\` when the change is ready.
1929
+ Use \`forge verify --standard\` for the normal agent development loop. Finish handoffs with \`${finalVerify}\` when the change is ready.
1483
1930
 
1484
1931
  ### Repair a failing check
1485
1932
 
@@ -1498,7 +1945,8 @@ Use:
1498
1945
 
1499
1946
  \`\`\`bash
1500
1947
  forge generate
1501
- forge inspect all --json
1948
+ forge inspect ai --json
1949
+ forge agent print-context --json
1502
1950
  forge ai check --json
1503
1951
  forge ai trace <traceId> --json
1504
1952
  \`\`\`
@@ -1537,7 +1985,7 @@ forge deps upgrade-plan <package> --to latest
1537
1985
  forge deps inspect <package> --json
1538
1986
  forge deps api <package> <symbol> --json
1539
1987
  forge deps upgrade-apply <plan>
1540
- forge verify --strict
1988
+ ${finalVerify}
1541
1989
  \`\`\`
1542
1990
 
1543
1991
  Do not manually edit \`package.json\` for package upgrades unless necessary.
@@ -1558,10 +2006,11 @@ Durable invalidations live in \`_forge_live_invalidations\`.
1558
2006
 
1559
2007
  ${AGENTS_USER_START}
1560
2008
 
1561
- ${userNotes}
2009
+ ${AGENTS_USER_NOTES_TOKEN}
1562
2010
 
1563
2011
  ${AGENTS_USER_END}
1564
2012
  `);
2013
+ return normalizeNewlines(generated.replace(AGENTS_USER_NOTES_TOKEN, () => userNotes));
1565
2014
  }
1566
2015
 
1567
2016
  function renderAppMapMd(contract: AgentContract): string {
@@ -1922,21 +2371,27 @@ function renderOperationPlaybooksMd(playbookEntries: AgentPlaybook[]): string {
1922
2371
  return normalizeNewlines(lines.join("\n"));
1923
2372
  }
1924
2373
 
1925
- function renderAgentQuickstartMd(): string {
1926
- return normalizeNewlines(`# Agent Quickstart
2374
+ function renderAgentQuickstartMd(workspaceRoot: string): string {
2375
+ const finalVerify = finalVerifyCommand(workspaceRoot);
2376
+ return normalizeNewlines(localizeForgeCliMarkdown(workspaceRoot, `# Agent Quickstart
1927
2377
 
1928
2378
  Run:
1929
2379
 
1930
2380
  \`\`\`bash
2381
+ forge agent onboard --target codex --json
2382
+ forge status --json
2383
+ forge changed --json
2384
+ forge handoff --json
1931
2385
  forge do inspect --json
1932
2386
  forge do fix --json
1933
2387
  forge do verify --json
1934
2388
  forge dev --once --json
1935
2389
  forge dev
1936
- forge inspect all --json
2390
+ forge agent print-context --json
1937
2391
  forge inspect frontend --json
1938
2392
  forge inspect capabilities --json
1939
2393
  forge inspect agent-tools --json
2394
+ forge inspect all --json
1940
2395
  forge check --json
1941
2396
  forge ai trace <traceId> --json
1942
2397
  \`\`\`
@@ -1954,7 +2409,158 @@ Always finish with:
1954
2409
 
1955
2410
  \`\`\`bash
1956
2411
  forge generate
1957
- forge verify --strict
2412
+ ${finalVerify}
1958
2413
  \`\`\`
1959
- `);
2414
+ `));
2415
+ }
2416
+
2417
+ function renderAgentCairGuideMd(contract: AgentContract, workspaceRoot: string): string {
2418
+ const cair = contract.agentProtocols.find((protocol) => protocol.id === "cair");
2419
+ const finalVerify = finalVerifyCommand(workspaceRoot);
2420
+ const summary = [
2421
+ `commands=${contract.commands.length}`,
2422
+ `queries=${contract.queries.length}`,
2423
+ `liveQueries=${contract.liveQueries.length}`,
2424
+ `actions=${contract.actions.length}`,
2425
+ `workflows=${contract.workflows.length}`,
2426
+ `tables=${contract.data.tables.length}`,
2427
+ ].join(" ");
2428
+ return normalizeNewlines(localizeForgeCliMarkdown(workspaceRoot, `# CAIR Agent Guide
2429
+
2430
+ Project: ${contract.project.name}
2431
+ CAIR version: ${cair?.version ?? CAIR_SCHEMA_VERSION}
2432
+ Surface: ${summary}
2433
+
2434
+ CAIR is the compact agent protocol for reading and changing this Forge workspace. Use it before opening whole files when symbol, module, dependency, test, or impact context is enough.
2435
+
2436
+ ## First commands
2437
+
2438
+ \`\`\`bash
2439
+ forge cair snapshot
2440
+ forge cair query "Q ST"
2441
+ \`\`\`
2442
+
2443
+ The snapshot emits compact ids:
2444
+
2445
+ - \`M#\` modules/files
2446
+ - \`S#\` symbols
2447
+ - \`P#\` packages
2448
+ - \`API#\` dependency APIs
2449
+ - \`T#\` tests
2450
+
2451
+ ## Read before editing
2452
+
2453
+ \`\`\`bash
2454
+ forge cair query "Q S name=<symbol>"
2455
+ forge cair query "Q D S#1"
2456
+ forge cair query "Q R S#1"
2457
+ forge cair query "Q I S#1"
2458
+ forge cair query "Q T S#1"
2459
+ forge cair query "Q DEP.API package=<pkg> symbol=<export>"
2460
+ \`\`\`
2461
+
2462
+ Only open source files after CAIR shows that the exact file or body is needed.
2463
+
2464
+ ## Plan, apply, rollback
2465
+
2466
+ Never apply semantic mutations first. Create a plan:
2467
+
2468
+ \`\`\`bash
2469
+ forge cair action --plan "A RN t=S#1 nn=<newName>"
2470
+ \`\`\`
2471
+
2472
+ Apply the returned plan path:
2473
+
2474
+ \`\`\`bash
2475
+ forge cair action "A APPLY plan=<P#|.forge/cair/plans/...json>"
2476
+ \`\`\`
2477
+
2478
+ Keep returned journal paths for rollback:
2479
+
2480
+ \`\`\`bash
2481
+ forge cair action "A ROLLBACK journal=.forge/cair/journal/<journal>.json"
2482
+ \`\`\`
2483
+
2484
+ ## Semantic actions
2485
+
2486
+ \`\`\`txt
2487
+ A RN t=S#1 nn=<newName>
2488
+ A MV t=S#1 to=src/target.ts
2489
+ A SIG t=S#1 signature="export function x(input: string): boolean"
2490
+ A PARAM t=S#1 name=tenantId type=string default="defaultTenant"
2491
+ A CALLS t=S#1 appendArg="defaultTenant"
2492
+ A OI f=M#1
2493
+ A FMT f=M#1
2494
+ \`\`\`
2495
+
2496
+ For high-risk semantic actions, include expectations when available:
2497
+
2498
+ \`\`\`txt
2499
+ expect.file=src/path.ts
2500
+ expect.kind=command
2501
+ expect.hash=<sha256>
2502
+ \`\`\`
2503
+
2504
+ ## Forge-native actions
2505
+
2506
+ Prefer Forge-native CAIR actions over hand-writing boilerplate:
2507
+
2508
+ \`\`\`txt
2509
+ A MC n=createTicket
2510
+ A MQ n=listTickets
2511
+ A MA n=chargeCustomer
2512
+ A MT n=tickets fields=title:text,status:text
2513
+ A AT t=S#1 kind=unit
2514
+ A WX t=S#1 file=src/index.ts
2515
+ \`\`\`
2516
+
2517
+ ## Compact aliases
2518
+
2519
+ Queries:
2520
+
2521
+ \`\`\`txt
2522
+ Q ST = Q STATUS
2523
+ Q S = Q SYMBOL
2524
+ Q D = Q DEF
2525
+ Q R = Q REFS
2526
+ Q I = Q IMPACT
2527
+ Q M = Q MODULE
2528
+ Q T = Q TESTS
2529
+ Q API = Q DEP.API
2530
+ \`\`\`
2531
+
2532
+ Actions:
2533
+
2534
+ \`\`\`txt
2535
+ A RN = A RENAME.SYMBOL
2536
+ A MV = A MOVE.SYMBOL
2537
+ A OI = A ORGANIZE.IMPORTS
2538
+ A FMT = A FORMAT
2539
+ A MC = A MAKE.COMMAND
2540
+ A MQ = A MAKE.QUERY
2541
+ A MA = A MAKE.ACTION
2542
+ A MT = A MAKE.TABLE
2543
+ A AT = A ADD.TEST
2544
+ A WX = A WIRE.EXPORT
2545
+ A AP = A APPLY
2546
+ A RB = A ROLLBACK
2547
+ \`\`\`
2548
+
2549
+ ## Verification
2550
+
2551
+ After CAIR edits, run the narrowest useful checks:
2552
+
2553
+ \`\`\`bash
2554
+ forge check --json
2555
+ forge verify --standard
2556
+ ${finalVerify}
2557
+ \`\`\`
2558
+
2559
+ ## Constraints
2560
+
2561
+ - Do not edit \`src/forge/_generated/**\` unless explicitly allowed.
2562
+ - Do not bypass \`--plan\` for semantic edits.
2563
+ - Do not use CAIR as blind text replacement when a semantic action exists.
2564
+ - Use TypeScript language service, ast-grep, ts-morph, or raw file reads only as implementation backends or fallbacks. CAIR is the agent-facing protocol.
2565
+ `));
1960
2566
  }