forgeos 0.1.0-alpha.4 → 0.1.0-alpha.41
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.
- package/.npmignore +10 -0
- package/AGENTS.md +142 -90
- package/CHANGELOG.md +433 -0
- package/LICENSE +21 -0
- package/README.md +67 -8
- package/adapters/go/README.md +23 -0
- package/adapters/go/go.mod +3 -0
- package/adapters/go/http.go +149 -0
- package/adapters/go/registry.go +234 -0
- package/adapters/go/types.go +136 -0
- package/adapters/java/README.md +68 -0
- package/adapters/java/pom.xml +34 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Auth.java +20 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Diagnostic.java +16 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Entry.java +38 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/EntryKind.java +16 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ErrorInfo.java +4 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Forge.java +94 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeCall.java +12 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeContext.java +11 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHandler.java +8 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeHttpHandler.java +179 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ForgeRegistry.java +121 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Json.java +14 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Manifest.java +14 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/RequestEnvelope.java +6 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/ResponseEnvelope.java +25 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Risk.java +18 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Schemas.java +36 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/Service.java +65 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/TransactionMode.java +18 -0
- package/adapters/java/src/main/java/dev/forgeos/adapter/TypedForgeHandler.java +6 -0
- package/adapters/java-spring-boot-starter/README.md +32 -0
- package/adapters/java-spring-boot-starter/pom.xml +36 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeCommand.java +22 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeExternalService.java +15 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeQuery.java +16 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeServiceBeanCondition.java +18 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringAutoConfiguration.java +16 -0
- package/adapters/java-spring-boot-starter/src/main/java/dev/forgeos/adapter/spring/ForgeSpringRuntime.java +104 -0
- package/adapters/java-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports +1 -0
- package/bin/forge.mjs +18 -0
- package/docs/cair-protocol.md +103 -0
- package/docs/changelog.md +548 -0
- package/docs/forge-protocol.md +189 -0
- package/examples/go-billing/go.mod +7 -0
- package/examples/go-billing/main.go +120 -0
- package/examples/java-billing/pom.xml +52 -0
- package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/CreateInvoiceInput.java +4 -0
- package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Invoice.java +11 -0
- package/examples/java-billing/src/main/java/dev/forgeos/examples/billing/Main.java +127 -0
- package/package.json +31 -3
- package/schemas/forge-manifest.schema.json +57 -0
- package/src/forge/_generated/releaseManifest.json +1 -2
- package/src/forge/_generated/releaseManifest.ts +3 -3
- package/src/forge/agent-adapters/index.ts +1584 -125
- package/src/forge/agent-adapters/types.ts +224 -1
- package/src/forge/agent-memory/bridge.ts +1333 -0
- package/src/forge/agent-memory/context-pack.ts +277 -0
- package/src/forge/agent-memory/hook-runner.ts +312 -0
- package/src/forge/agent-memory/mcp.ts +224 -0
- package/src/forge/agent-memory/normalize.ts +498 -0
- package/src/forge/agent-memory/redaction.ts +103 -0
- package/src/forge/agent-memory/sources/claude-code.ts +51 -0
- package/src/forge/agent-memory/sources/codex-hook-runner.mjs +273 -0
- package/src/forge/agent-memory/sources/codex.ts +119 -0
- package/src/forge/agent-memory/sources/cursor.ts +35 -0
- package/src/forge/agent-memory/types.ts +204 -0
- package/src/forge/bench.ts +248 -0
- package/src/forge/brownfield-import/index.ts +801 -0
- package/src/forge/brownfield-import/types.ts +127 -0
- package/src/forge/cair/action-journal.ts +61 -0
- package/src/forge/cair/action-parser.ts +314 -0
- package/src/forge/cair/action-validator.ts +40 -0
- package/src/forge/cair/actions.ts +1818 -0
- package/src/forge/cair/format.ts +77 -0
- package/src/forge/cair/index.ts +106 -0
- package/src/forge/cair/query.ts +478 -0
- package/src/forge/cair/snapshot.ts +315 -0
- package/src/forge/cair/types.ts +248 -0
- package/src/forge/cli/ai.ts +339 -21
- package/src/forge/cli/auth.ts +348 -10
- package/src/forge/cli/authmd.ts +441 -0
- package/src/forge/cli/baseline.ts +112 -0
- package/src/forge/cli/build.ts +20 -4
- package/src/forge/cli/changed.ts +437 -0
- package/src/forge/cli/codex-app-server.ts +877 -0
- package/src/forge/cli/commands.ts +1620 -20
- package/src/forge/cli/db.ts +209 -2
- package/src/forge/cli/deploy.ts +887 -0
- package/src/forge/cli/deps.ts +82 -13
- package/src/forge/cli/dev.ts +961 -46
- package/src/forge/cli/docs.ts +265 -0
- package/src/forge/cli/doctor.ts +297 -0
- package/src/forge/cli/field-test.ts +329 -0
- package/src/forge/cli/handoff.ts +336 -0
- package/src/forge/cli/index.ts +1 -0
- package/src/forge/cli/last-run.ts +84 -0
- package/src/forge/cli/main.ts +107 -3
- package/src/forge/cli/new.ts +152 -13
- package/src/forge/cli/next-actions.ts +23 -0
- package/src/forge/cli/output.ts +389 -6
- package/src/forge/cli/parse.ts +1050 -34
- package/src/forge/cli/progress.ts +51 -0
- package/src/forge/cli/query.ts +32 -0
- package/src/forge/cli/release.ts +35 -11
- package/src/forge/cli/rls.ts +43 -4
- package/src/forge/cli/run.ts +41 -0
- package/src/forge/cli/security.ts +112 -0
- package/src/forge/cli/self-host.ts +56 -14
- package/src/forge/cli/studio.ts +2218 -0
- package/src/forge/cli/verify.ts +1455 -32
- package/src/forge/cli/windows.ts +23 -0
- package/src/forge/cli/workos.ts +1020 -0
- package/src/forge/compiler/agent-contract/build.ts +670 -64
- package/src/forge/compiler/agent-contract/types.ts +45 -2
- package/src/forge/compiler/ai-registry/parse.ts +71 -2
- package/src/forge/compiler/api-surface/build.ts +47 -0
- package/src/forge/compiler/app-graph/build.ts +112 -9
- package/src/forge/compiler/app-graph/extract.ts +107 -0
- package/src/forge/compiler/app-graph/module-graph.ts +73 -78
- package/src/forge/compiler/app-graph/parser.ts +24 -24
- package/src/forge/compiler/app-graph/profile.ts +26 -0
- package/src/forge/compiler/app-graph/versions.ts +1 -1
- package/src/forge/compiler/classifier/capabilities.ts +3 -2
- package/src/forge/compiler/classifier/classify.ts +32 -8
- package/src/forge/compiler/classifier/secrets.ts +3 -2
- package/src/forge/compiler/classifier/signals.ts +91 -1
- package/src/forge/compiler/client-sdk/build-manifest.ts +59 -0
- package/src/forge/compiler/client-sdk/render-client.ts +295 -13
- package/src/forge/compiler/data-graph/parse.ts +20 -4
- package/src/forge/compiler/data-graph/sql/ddl.ts +144 -31
- package/src/forge/compiler/data-graph/sql/serialize.ts +4 -0
- package/src/forge/compiler/data-graph/sql/types.ts +1 -0
- package/src/forge/compiler/diagnostics/codes.ts +30 -0
- package/src/forge/compiler/diagnostics/create.ts +21 -2
- package/src/forge/compiler/diagnostics/index.ts +2 -0
- package/src/forge/compiler/emitter/barrel.ts +3 -0
- package/src/forge/compiler/emitter/render.ts +9 -0
- package/src/forge/compiler/external-manifest/registry.ts +205 -0
- package/src/forge/compiler/external-manifest/types.ts +91 -0
- package/src/forge/compiler/external-manifest/validate.ts +373 -0
- package/src/forge/compiler/frontend-graph/build.ts +142 -14
- package/src/forge/compiler/integration/add.ts +618 -24
- package/src/forge/compiler/integration/plan.ts +43 -12
- package/src/forge/compiler/integration/render.ts +29 -0
- package/src/forge/compiler/integration/snapshot.ts +2 -0
- package/src/forge/compiler/integration/templates/convex.ts +70 -0
- package/src/forge/compiler/integration/templates/index.ts +1 -0
- package/src/forge/compiler/integration/templates/render.ts +38 -0
- package/src/forge/compiler/integration/templates/types.ts +3 -0
- package/src/forge/compiler/integration/templates/workos.ts +1219 -0
- package/src/forge/compiler/make-registry/build.ts +8 -9
- package/src/forge/compiler/orchestrator/fast-check.ts +4 -0
- package/src/forge/compiler/orchestrator/generate-lock.ts +14 -3
- package/src/forge/compiler/orchestrator/manifest.ts +1 -1
- package/src/forge/compiler/orchestrator/plan-profile.ts +23 -0
- package/src/forge/compiler/orchestrator/plan.ts +247 -10
- package/src/forge/compiler/orchestrator/profile.ts +65 -0
- package/src/forge/compiler/orchestrator/run.ts +97 -31
- package/src/forge/compiler/orchestrator/serialize.ts +33 -8
- package/src/forge/compiler/orchestrator/types.ts +1 -1
- package/src/forge/compiler/package-graph/compiler.ts +3 -1
- package/src/forge/compiler/package-graph/constants.ts +1 -1
- package/src/forge/compiler/package-manager/adapter.ts +4 -1
- package/src/forge/compiler/package-manager/commands.ts +4 -0
- package/src/forge/compiler/package-manager/executor.ts +30 -1
- package/src/forge/compiler/package-manager/version.ts +36 -12
- package/src/forge/compiler/package-upgrades/planner.ts +155 -18
- package/src/forge/compiler/package-upgrades/types.ts +2 -0
- package/src/forge/compiler/policy-registry/build.ts +47 -2
- package/src/forge/compiler/policy-registry/parse.ts +32 -2
- package/src/forge/compiler/recipes/definitions.ts +63 -0
- package/src/forge/compiler/recipes/index.ts +2 -0
- package/src/forge/compiler/recipes/registry.ts +6 -0
- package/src/forge/compiler/test-graph/build.ts +11 -3
- package/src/forge/compiler/types/app-graph.ts +8 -2
- package/src/forge/compiler/types/cli.ts +75 -1
- package/src/forge/compiler/types/dev-manifest.ts +4 -0
- package/src/forge/compiler/types/emit.ts +2 -0
- package/src/forge/compiler/types/frontend-graph.ts +2 -2
- package/src/forge/compiler/types/integration.ts +1 -0
- package/src/forge/compiler/types/package-graph.ts +6 -0
- package/src/forge/compiler/types/policy-registry.ts +3 -1
- package/src/forge/delta/classifier.ts +52 -0
- package/src/forge/delta/explain.ts +238 -0
- package/src/forge/delta/git-observer.ts +43 -0
- package/src/forge/delta/ids.ts +44 -0
- package/src/forge/delta/index.ts +25 -0
- package/src/forge/delta/recorder.ts +540 -0
- package/src/forge/delta/redaction.ts +50 -0
- package/src/forge/delta/schema.ts +240 -0
- package/src/forge/delta/session.ts +142 -0
- package/src/forge/delta/status.ts +1202 -0
- package/src/forge/delta/store.ts +3284 -0
- package/src/forge/delta/timeline.ts +178 -0
- package/src/forge/dev/server.ts +808 -17
- package/src/forge/dev/types.ts +15 -1
- package/src/forge/dev/watch.ts +17 -7
- package/src/forge/dev-console/cycle.ts +280 -33
- package/src/forge/dev-console/types.ts +47 -1
- package/src/forge/impact/index.ts +204 -9
- package/src/forge/impact/types.ts +33 -1
- package/src/forge/intent/index.ts +35 -16
- package/src/forge/make/fields.ts +26 -0
- package/src/forge/make/index.ts +40 -6
- package/src/forge/make/templates.ts +155 -2
- package/src/forge/make/types.ts +2 -1
- package/src/forge/policy.ts +1 -1
- package/src/forge/react/index.ts +160 -2
- package/src/forge/refactor/index.ts +1 -0
- package/src/forge/repair/rules/index.ts +2 -2
- package/src/forge/review/index.ts +158 -12
- package/src/forge/review/types.ts +15 -0
- package/src/forge/runtime/auth/config.ts +17 -0
- package/src/forge/runtime/auth/evaluate.ts +15 -2
- package/src/forge/runtime/auth/resolve.ts +61 -6
- package/src/forge/runtime/auth/types.ts +1 -0
- package/src/forge/runtime/db/factory.ts +1 -3
- package/src/forge/runtime/db/generated-client.ts +13 -2
- package/src/forge/runtime/db/memory-adapter.ts +139 -32
- package/src/forge/runtime/db/pglite-adapter.ts +263 -2
- package/src/forge/runtime/db/postgres-adapter.ts +6 -3
- package/src/forge/runtime/executor.ts +129 -16
- package/src/forge/runtime/external/bridge.ts +649 -0
- package/src/forge/runtime/runner/run-entry.ts +16 -7
- package/src/forge/runtime/telemetry/buffer.ts +4 -1
- package/src/forge/runtime/telemetry/scrubber.ts +35 -5
- package/src/forge/runtime/webhooks/security.ts +12 -7
- package/src/forge/server.ts +17 -3
- package/src/forge/ui/index.ts +209 -0
- package/src/forge/ui/types.ts +1 -0
- package/src/forge/version.ts +1 -1
- package/src/forge/vue/index.ts +422 -0
- package/src/forge/workspace/baseline.ts +112 -0
- package/src/forge/workspace/change-summary.ts +250 -0
- package/src/forge/workspace/forge-cli.ts +42 -0
- package/src/forge/workspace/git-summary.ts +391 -0
- package/templates/agent-workroom/AGENTS.md +29 -0
- package/templates/agent-workroom/README.md +34 -0
- package/templates/agent-workroom/forge.config.ts +3 -0
- package/templates/agent-workroom/package.json +33 -0
- package/templates/agent-workroom/src/actions/indexAgentSignal.ts +10 -0
- package/templates/agent-workroom/src/commands/openWorkroom.ts +61 -0
- package/templates/agent-workroom/src/commands/recordAgentSignal.ts +119 -0
- package/templates/agent-workroom/src/commands/recordCheckRun.ts +52 -0
- package/templates/agent-workroom/src/forge/schema.ts +54 -0
- package/templates/agent-workroom/src/policies.ts +6 -0
- package/templates/agent-workroom/src/queries/listWorkrooms.ts +11 -0
- package/templates/agent-workroom/src/queries/liveWorkroom.ts +63 -0
- package/templates/agent-workroom/tsconfig.json +16 -0
- package/templates/agent-workroom/web/index.html +12 -0
- package/templates/agent-workroom/web/package.json +21 -0
- package/templates/agent-workroom/web/src/App.tsx +345 -0
- package/templates/agent-workroom/web/src/lib/forge.ts +13 -0
- package/templates/agent-workroom/web/src/main.tsx +13 -0
- package/templates/agent-workroom/web/src/styles.css +545 -0
- package/templates/agent-workroom/web/tsconfig.json +27 -0
- package/templates/b2b-support-web/package.json +1 -0
- package/templates/b2b-support-web/web/package.json +1 -1
- package/templates/minimal-web/package.json +1 -1
- package/templates/minimal-web/web/package.json +2 -2
- package/templates/nuxt-web/.vscode/settings.json +14 -0
- package/templates/nuxt-web/README.md +30 -0
- package/templates/nuxt-web/forge.config.ts +3 -0
- package/templates/nuxt-web/package.json +33 -0
- package/templates/nuxt-web/src/actions/logNoteCreated.ts +11 -0
- package/templates/nuxt-web/src/commands/createNote.ts +26 -0
- package/templates/nuxt-web/src/forge/schema.ts +12 -0
- package/templates/nuxt-web/src/policies.ts +6 -0
- package/templates/nuxt-web/src/queries/listNotes.ts +8 -0
- package/templates/nuxt-web/src/queries/liveNotes.ts +8 -0
- package/templates/nuxt-web/tsconfig.json +17 -0
- package/templates/nuxt-web/web/app.vue +67 -0
- package/templates/nuxt-web/web/components/LiveNotes.vue +89 -0
- package/templates/nuxt-web/web/components/NoteComposer.vue +100 -0
- package/templates/nuxt-web/web/composables/forge.ts +13 -0
- package/templates/nuxt-web/web/composables/useNotes.ts +24 -0
- package/templates/nuxt-web/web/nuxt.config.ts +11 -0
- package/templates/nuxt-web/web/package.json +18 -0
- package/templates/nuxt-web/web/plugins/forge.client.ts +10 -0
- package/templates/nuxt-web/web/plugins/forge.server.ts +10 -0
- package/templates/nuxt-web/web/server/api/forge-health.get.ts +7 -0
- package/templates/nuxt-web/web/tsconfig.json +6 -0
- package/src/forge/_generated/actionSubscriptions.json +0 -2
- package/src/forge/_generated/actionSubscriptions.ts +0 -10
- package/src/forge/_generated/agentAdapterManifest.json +0 -2
- package/src/forge/_generated/agentAdapterManifest.ts +0 -73
- package/src/forge/_generated/agentContract.json +0 -2
- package/src/forge/_generated/agentContract.ts +0 -7829
- package/src/forge/_generated/agentQuickstart.md +0 -34
- package/src/forge/_generated/agentTools.json +0 -2
- package/src/forge/_generated/agentTools.md +0 -16
- package/src/forge/_generated/agentTools.ts +0 -12
- package/src/forge/_generated/aiContext.ts +0 -125
- package/src/forge/_generated/aiModels.json +0 -2
- package/src/forge/_generated/aiModels.ts +0 -51
- package/src/forge/_generated/aiProviders.json +0 -2
- package/src/forge/_generated/aiProviders.ts +0 -23
- package/src/forge/_generated/aiRegistry.json +0 -2
- package/src/forge/_generated/aiRegistry.ts +0 -31
- package/src/forge/_generated/api.json +0 -2
- package/src/forge/_generated/api.ts +0 -8
- package/src/forge/_generated/appGraph.json +0 -2
- package/src/forge/_generated/appGraph.ts +0 -14919
- package/src/forge/_generated/appMap.md +0 -55
- package/src/forge/_generated/artifactManifest.json +0 -2
- package/src/forge/_generated/artifactManifest.ts +0 -7
- package/src/forge/_generated/authClaims.json +0 -2
- package/src/forge/_generated/authClaims.ts +0 -13
- package/src/forge/_generated/authConfig.json +0 -2
- package/src/forge/_generated/authConfig.ts +0 -17
- package/src/forge/_generated/authContext.ts +0 -23
- package/src/forge/_generated/authRegistry.json +0 -2
- package/src/forge/_generated/authRegistry.ts +0 -25
- package/src/forge/_generated/buildInfo.json +0 -2
- package/src/forge/_generated/buildInfo.ts +0 -9
- package/src/forge/_generated/capabilityMap.json +0 -2
- package/src/forge/_generated/capabilityMap.md +0 -15
- package/src/forge/_generated/capabilityMap.ts +0 -17
- package/src/forge/_generated/client.ts +0 -282
- package/src/forge/_generated/clientApi.ts +0 -9
- package/src/forge/_generated/clientManifest.json +0 -2
- package/src/forge/_generated/clientManifest.ts +0 -39
- package/src/forge/_generated/clientTypes.ts +0 -78
- package/src/forge/_generated/configRegistry.json +0 -2
- package/src/forge/_generated/configRegistry.ts +0 -4
- package/src/forge/_generated/dataGraph.json +0 -2
- package/src/forge/_generated/dataGraph.ts +0 -8
- package/src/forge/_generated/db.json +0 -2
- package/src/forge/_generated/db.ts +0 -2
- package/src/forge/_generated/dbSecurityManifest.json +0 -2
- package/src/forge/_generated/dbSecurityManifest.ts +0 -15
- package/src/forge/_generated/dbSessionContext.json +0 -2
- package/src/forge/_generated/dbSessionContext.ts +0 -39
- package/src/forge/_generated/deployManifest.json +0 -2
- package/src/forge/_generated/deployManifest.ts +0 -14
- package/src/forge/_generated/devManifest.json +0 -2
- package/src/forge/_generated/devManifest.ts +0 -62
- package/src/forge/_generated/envSchema.json +0 -2
- package/src/forge/_generated/envSchema.ts +0 -59
- package/src/forge/_generated/frontendGraph.json +0 -2
- package/src/forge/_generated/frontendGraph.ts +0 -27
- package/src/forge/_generated/importGuards.json +0 -2
- package/src/forge/_generated/importGuards.ts +0 -686
- package/src/forge/_generated/index.ts +0 -68
- package/src/forge/_generated/liveProductionManifest.json +0 -2
- package/src/forge/_generated/liveProductionManifest.ts +0 -23
- package/src/forge/_generated/liveProtocol.json +0 -2
- package/src/forge/_generated/liveProtocol.ts +0 -21
- package/src/forge/_generated/liveQueryRegistry.json +0 -2
- package/src/forge/_generated/liveQueryRegistry.ts +0 -9
- package/src/forge/_generated/liveTransportConfig.json +0 -2
- package/src/forge/_generated/liveTransportConfig.ts +0 -19
- package/src/forge/_generated/makeRegistry.json +0 -2
- package/src/forge/_generated/makeRegistry.ts +0 -177
- package/src/forge/_generated/makeTemplates.json +0 -2
- package/src/forge/_generated/makeTemplates.ts +0 -66
- package/src/forge/_generated/mockMap.json +0 -2
- package/src/forge/_generated/mockMap.ts +0 -7
- package/src/forge/_generated/operationPlaybooks.md +0 -167
- package/src/forge/_generated/packageGraph.json +0 -2
- package/src/forge/_generated/packageGraph.ts +0 -249334
- package/src/forge/_generated/packageUpgradeRegistry.json +0 -2
- package/src/forge/_generated/packageUpgradeRegistry.ts +0 -15
- package/src/forge/_generated/permissionMatrix.json +0 -2
- package/src/forge/_generated/permissionMatrix.ts +0 -7
- package/src/forge/_generated/policyRegistry.json +0 -2
- package/src/forge/_generated/policyRegistry.ts +0 -11
- package/src/forge/_generated/queryRegistry.json +0 -2
- package/src/forge/_generated/queryRegistry.ts +0 -9
- package/src/forge/_generated/react.d.ts +0 -22
- package/src/forge/_generated/react.ts +0 -29
- package/src/forge/_generated/reactManifest.json +0 -2
- package/src/forge/_generated/reactManifest.ts +0 -19
- package/src/forge/_generated/rlsPolicies.json +0 -2
- package/src/forge/_generated/rlsPolicies.sql +0 -34
- package/src/forge/_generated/rlsPolicies.ts +0 -6
- package/src/forge/_generated/runtimeGraph.json +0 -2
- package/src/forge/_generated/runtimeGraph.ts +0 -8
- package/src/forge/_generated/runtimeMatrix.json +0 -2
- package/src/forge/_generated/runtimeMatrix.ts +0 -334130
- package/src/forge/_generated/runtimeRegistry.ts +0 -2
- package/src/forge/_generated/runtimeRules.md +0 -91
- package/src/forge/_generated/secretRegistry.json +0 -2
- package/src/forge/_generated/secretRegistry.ts +0 -50
- package/src/forge/_generated/secretsContext.ts +0 -11
- package/src/forge/_generated/serverApi.ts +0 -10
- package/src/forge/_generated/sourceMapManifest.json +0 -2
- package/src/forge/_generated/sourceMapManifest.ts +0 -7
- package/src/forge/_generated/sqlPlan.json +0 -2
- package/src/forge/_generated/sqlPlan.ts +0 -88
- package/src/forge/_generated/subscriptionManifest.json +0 -2
- package/src/forge/_generated/subscriptionManifest.ts +0 -7
- package/src/forge/_generated/symbolicationManifest.json +0 -2
- package/src/forge/_generated/symbolicationManifest.ts +0 -17
- package/src/forge/_generated/telemetryRegistry.json +0 -2
- package/src/forge/_generated/telemetryRegistry.ts +0 -9
- package/src/forge/_generated/telemetrySinks.json +0 -2
- package/src/forge/_generated/telemetrySinks.ts +0 -11
- package/src/forge/_generated/tenantScope.json +0 -2
- package/src/forge/_generated/tenantScope.ts +0 -8
- package/src/forge/_generated/testGraph.json +0 -2
- package/src/forge/_generated/testGraph.ts +0 -3430
- package/src/forge/_generated/testPlanRegistry.json +0 -2
- package/src/forge/_generated/testPlanRegistry.ts +0 -33
- package/src/forge/_generated/uiRoutes.json +0 -2
- package/src/forge/_generated/uiRoutes.ts +0 -16
- package/src/forge/_generated/uiScenarios.json +0 -2
- package/src/forge/_generated/uiScenarios.ts +0 -30
- package/src/forge/_generated/uiTestManifest.json +0 -2
- package/src/forge/_generated/uiTestManifest.ts +0 -27
- package/src/forge/_generated/workflowRegistry.json +0 -2
- package/src/forge/_generated/workflowRegistry.ts +0 -9
- package/src/forge/_generated/workflowSubscriptions.json +0 -2
- 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
|
-
|
|
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
|
-
|
|
183
|
-
|
|
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
|
-
|
|
379
|
+
tableLookup: Map<string, string>,
|
|
193
380
|
ops: Set<string>,
|
|
194
381
|
): string[] {
|
|
195
|
-
return dbTablesForText(
|
|
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 =
|
|
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", "
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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: [
|
|
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: [
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
919
|
-
const
|
|
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,
|
|
927
|
-
tablesWritten: dbTablesForFile(input.workspaceRoot, file,
|
|
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
|
|
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,
|
|
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,
|
|
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
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
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
|
|
1146
|
-
afterEditing: ["forge generate", "forge check", "forge verify --standard",
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
1392
|
-
- Mount \`<ForgeProvider devAuth>\` in local development.
|
|
1393
|
-
- Use \`useQuery
|
|
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
|
|
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
|
|
1424
|
-
forge make resource <name> --fields title:text,status:enum
|
|
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,
|
|
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 \`
|
|
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
|
|
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
|
-
|
|
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
|
-
${
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
}
|