forgeos 0.1.0-alpha.0
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 +1 -0
- package/AGENTS.md +277 -0
- package/CHANGELOG.md +8 -0
- package/CONTRIBUTING.md +58 -0
- package/README.md +377 -0
- package/bin/forge-bun.mjs +110 -0
- package/bin/forge.mjs +19 -0
- package/package.json +96 -0
- package/packages/eslint-plugin-forge/index.ts +15 -0
- package/packages/eslint-plugin-forge/package.json +10 -0
- package/packages/eslint-plugin-forge/src/check-source.ts +95 -0
- package/packages/eslint-plugin-forge/src/load-artifacts.ts +24 -0
- package/packages/eslint-plugin-forge/src/rule-no-forge-guard-violation.ts +93 -0
- package/src/forge/_generated/actionSubscriptions.json +2 -0
- package/src/forge/_generated/actionSubscriptions.ts +10 -0
- package/src/forge/_generated/agentAdapterManifest.json +2 -0
- package/src/forge/_generated/agentAdapterManifest.ts +73 -0
- package/src/forge/_generated/agentContract.json +2 -0
- package/src/forge/_generated/agentContract.ts +912 -0
- package/src/forge/_generated/agentQuickstart.md +32 -0
- package/src/forge/_generated/aiContext.ts +59 -0
- package/src/forge/_generated/aiModels.json +2 -0
- package/src/forge/_generated/aiModels.ts +35 -0
- package/src/forge/_generated/aiProviders.json +2 -0
- package/src/forge/_generated/aiProviders.ts +23 -0
- package/src/forge/_generated/aiRegistry.json +2 -0
- package/src/forge/_generated/aiRegistry.ts +29 -0
- package/src/forge/_generated/api.json +2 -0
- package/src/forge/_generated/api.ts +8 -0
- package/src/forge/_generated/appGraph.json +2 -0
- package/src/forge/_generated/appGraph.ts +14511 -0
- package/src/forge/_generated/appMap.md +35 -0
- package/src/forge/_generated/artifactManifest.json +2 -0
- package/src/forge/_generated/artifactManifest.ts +7 -0
- package/src/forge/_generated/authClaims.json +2 -0
- package/src/forge/_generated/authClaims.ts +13 -0
- package/src/forge/_generated/authConfig.json +2 -0
- package/src/forge/_generated/authConfig.ts +17 -0
- package/src/forge/_generated/authContext.ts +23 -0
- package/src/forge/_generated/authRegistry.json +2 -0
- package/src/forge/_generated/authRegistry.ts +25 -0
- package/src/forge/_generated/buildInfo.json +2 -0
- package/src/forge/_generated/buildInfo.ts +9 -0
- package/src/forge/_generated/capabilityMap.json +2 -0
- package/src/forge/_generated/capabilityMap.md +15 -0
- package/src/forge/_generated/capabilityMap.ts +17 -0
- package/src/forge/_generated/client.ts +282 -0
- package/src/forge/_generated/clientApi.ts +9 -0
- package/src/forge/_generated/clientManifest.json +2 -0
- package/src/forge/_generated/clientManifest.ts +39 -0
- package/src/forge/_generated/clientTypes.ts +78 -0
- package/src/forge/_generated/configRegistry.json +2 -0
- package/src/forge/_generated/configRegistry.ts +4 -0
- package/src/forge/_generated/dataGraph.json +2 -0
- package/src/forge/_generated/dataGraph.ts +8 -0
- package/src/forge/_generated/db.json +2 -0
- package/src/forge/_generated/db.ts +2 -0
- package/src/forge/_generated/dbSecurityManifest.json +2 -0
- package/src/forge/_generated/dbSecurityManifest.ts +15 -0
- package/src/forge/_generated/dbSessionContext.json +2 -0
- package/src/forge/_generated/dbSessionContext.ts +39 -0
- package/src/forge/_generated/deployManifest.json +2 -0
- package/src/forge/_generated/deployManifest.ts +14 -0
- package/src/forge/_generated/devManifest.json +2 -0
- package/src/forge/_generated/devManifest.ts +47 -0
- package/src/forge/_generated/envSchema.json +2 -0
- package/src/forge/_generated/envSchema.ts +59 -0
- package/src/forge/_generated/frontendGraph.json +2 -0
- package/src/forge/_generated/frontendGraph.ts +27 -0
- package/src/forge/_generated/importGuards.json +2 -0
- package/src/forge/_generated/importGuards.ts +652 -0
- package/src/forge/_generated/index.ts +67 -0
- package/src/forge/_generated/liveProductionManifest.json +2 -0
- package/src/forge/_generated/liveProductionManifest.ts +23 -0
- package/src/forge/_generated/liveProtocol.json +2 -0
- package/src/forge/_generated/liveProtocol.ts +21 -0
- package/src/forge/_generated/liveQueryRegistry.json +2 -0
- package/src/forge/_generated/liveQueryRegistry.ts +9 -0
- package/src/forge/_generated/liveTransportConfig.json +2 -0
- package/src/forge/_generated/liveTransportConfig.ts +19 -0
- package/src/forge/_generated/makeRegistry.json +2 -0
- package/src/forge/_generated/makeRegistry.ts +163 -0
- package/src/forge/_generated/makeTemplates.json +2 -0
- package/src/forge/_generated/makeTemplates.ts +61 -0
- package/src/forge/_generated/mockMap.json +2 -0
- package/src/forge/_generated/mockMap.ts +7 -0
- package/src/forge/_generated/operationPlaybooks.md +145 -0
- package/src/forge/_generated/packageGraph.json +2 -0
- package/src/forge/_generated/packageGraph.ts +168569 -0
- package/src/forge/_generated/packageUpgradeRegistry.json +2 -0
- package/src/forge/_generated/packageUpgradeRegistry.ts +15 -0
- package/src/forge/_generated/permissionMatrix.json +2 -0
- package/src/forge/_generated/permissionMatrix.ts +7 -0
- package/src/forge/_generated/policyRegistry.json +2 -0
- package/src/forge/_generated/policyRegistry.ts +11 -0
- package/src/forge/_generated/queryRegistry.json +2 -0
- package/src/forge/_generated/queryRegistry.ts +9 -0
- package/src/forge/_generated/react.d.ts +22 -0
- package/src/forge/_generated/react.ts +29 -0
- package/src/forge/_generated/reactManifest.json +2 -0
- package/src/forge/_generated/reactManifest.ts +19 -0
- package/src/forge/_generated/releaseManifest.json +2 -0
- package/src/forge/_generated/releaseManifest.ts +25 -0
- package/src/forge/_generated/rlsPolicies.json +2 -0
- package/src/forge/_generated/rlsPolicies.sql +34 -0
- package/src/forge/_generated/rlsPolicies.ts +6 -0
- package/src/forge/_generated/runtimeGraph.json +2 -0
- package/src/forge/_generated/runtimeGraph.ts +8 -0
- package/src/forge/_generated/runtimeMatrix.json +2 -0
- package/src/forge/_generated/runtimeMatrix.ts +229125 -0
- package/src/forge/_generated/runtimeRegistry.ts +2 -0
- package/src/forge/_generated/runtimeRules.md +79 -0
- package/src/forge/_generated/secretRegistry.json +2 -0
- package/src/forge/_generated/secretRegistry.ts +50 -0
- package/src/forge/_generated/secretsContext.ts +11 -0
- package/src/forge/_generated/serverApi.ts +10 -0
- package/src/forge/_generated/sourceMapManifest.json +2 -0
- package/src/forge/_generated/sourceMapManifest.ts +7 -0
- package/src/forge/_generated/sqlPlan.json +2 -0
- package/src/forge/_generated/sqlPlan.ts +88 -0
- package/src/forge/_generated/subscriptionManifest.json +2 -0
- package/src/forge/_generated/subscriptionManifest.ts +7 -0
- package/src/forge/_generated/symbolicationManifest.json +2 -0
- package/src/forge/_generated/symbolicationManifest.ts +17 -0
- package/src/forge/_generated/telemetryRegistry.json +2 -0
- package/src/forge/_generated/telemetryRegistry.ts +9 -0
- package/src/forge/_generated/telemetrySinks.json +2 -0
- package/src/forge/_generated/telemetrySinks.ts +11 -0
- package/src/forge/_generated/tenantScope.json +2 -0
- package/src/forge/_generated/tenantScope.ts +8 -0
- package/src/forge/_generated/testGraph.json +2 -0
- package/src/forge/_generated/testGraph.ts +3054 -0
- package/src/forge/_generated/testPlanRegistry.json +2 -0
- package/src/forge/_generated/testPlanRegistry.ts +33 -0
- package/src/forge/_generated/uiRoutes.json +2 -0
- package/src/forge/_generated/uiRoutes.ts +16 -0
- package/src/forge/_generated/uiScenarios.json +2 -0
- package/src/forge/_generated/uiScenarios.ts +30 -0
- package/src/forge/_generated/uiTestManifest.json +2 -0
- package/src/forge/_generated/uiTestManifest.ts +27 -0
- package/src/forge/_generated/workflowRegistry.json +2 -0
- package/src/forge/_generated/workflowRegistry.ts +9 -0
- package/src/forge/_generated/workflowSubscriptions.json +2 -0
- package/src/forge/_generated/workflowSubscriptions.ts +10 -0
- package/src/forge/agent-adapters/index.ts +1002 -0
- package/src/forge/agent-adapters/types.ts +135 -0
- package/src/forge/cli/agent-contract.ts +50 -0
- package/src/forge/cli/ai.ts +148 -0
- package/src/forge/cli/auth.ts +198 -0
- package/src/forge/cli/build.ts +105 -0
- package/src/forge/cli/bun-exec.ts +4 -0
- package/src/forge/cli/commands.ts +1130 -0
- package/src/forge/cli/db.ts +316 -0
- package/src/forge/cli/deps.ts +277 -0
- package/src/forge/cli/dev.ts +529 -0
- package/src/forge/cli/doctor.ts +209 -0
- package/src/forge/cli/feature.ts +485 -0
- package/src/forge/cli/index.ts +25 -0
- package/src/forge/cli/lint-forge.ts +119 -0
- package/src/forge/cli/live.ts +179 -0
- package/src/forge/cli/main.ts +92 -0
- package/src/forge/cli/make.ts +133 -0
- package/src/forge/cli/new.ts +505 -0
- package/src/forge/cli/outbox.ts +297 -0
- package/src/forge/cli/output.ts +114 -0
- package/src/forge/cli/parse.ts +2211 -0
- package/src/forge/cli/policy.ts +204 -0
- package/src/forge/cli/query.ts +91 -0
- package/src/forge/cli/refactor.ts +221 -0
- package/src/forge/cli/release.ts +285 -0
- package/src/forge/cli/rls.ts +322 -0
- package/src/forge/cli/run.ts +76 -0
- package/src/forge/cli/secrets.ts +274 -0
- package/src/forge/cli/self-host.ts +468 -0
- package/src/forge/cli/serve.ts +93 -0
- package/src/forge/cli/telemetry.ts +219 -0
- package/src/forge/cli/verify.ts +587 -0
- package/src/forge/cli/version.ts +1 -0
- package/src/forge/cli/windows.ts +413 -0
- package/src/forge/cli/worker.ts +87 -0
- package/src/forge/cli/workflow.ts +424 -0
- package/src/forge/compiler/action-subscriptions/build.ts +116 -0
- package/src/forge/compiler/action-subscriptions/constants.ts +2 -0
- package/src/forge/compiler/action-subscriptions/index.ts +6 -0
- package/src/forge/compiler/action-subscriptions/parse.ts +6 -0
- package/src/forge/compiler/agent-contract/build.ts +1651 -0
- package/src/forge/compiler/agent-contract/types.ts +326 -0
- package/src/forge/compiler/ai-registry/build.ts +165 -0
- package/src/forge/compiler/ai-registry/constants.ts +2 -0
- package/src/forge/compiler/ai-registry/parse.ts +56 -0
- package/src/forge/compiler/api-surface/build.ts +107 -0
- package/src/forge/compiler/app-graph/build.ts +121 -0
- package/src/forge/compiler/app-graph/classify.ts +10 -0
- package/src/forge/compiler/app-graph/dup-symbol.ts +29 -0
- package/src/forge/compiler/app-graph/extract.ts +124 -0
- package/src/forge/compiler/app-graph/forge-apis.ts +29 -0
- package/src/forge/compiler/app-graph/index.ts +15 -0
- package/src/forge/compiler/app-graph/module-graph.ts +320 -0
- package/src/forge/compiler/app-graph/parser.ts +119 -0
- package/src/forge/compiler/app-graph/symbols.ts +48 -0
- package/src/forge/compiler/app-graph/tsconfig-hash.ts +62 -0
- package/src/forge/compiler/app-graph/types.ts +43 -0
- package/src/forge/compiler/app-graph/versions.ts +14 -0
- package/src/forge/compiler/cache/index.ts +17 -0
- package/src/forge/compiler/cache/key.ts +46 -0
- package/src/forge/compiler/cache/scheduler.ts +72 -0
- package/src/forge/compiler/cache/store.ts +78 -0
- package/src/forge/compiler/classifier/capabilities.ts +78 -0
- package/src/forge/compiler/classifier/classify.ts +113 -0
- package/src/forge/compiler/classifier/contexts.ts +188 -0
- package/src/forge/compiler/classifier/index.ts +18 -0
- package/src/forge/compiler/classifier/runtime-matrix.ts +45 -0
- package/src/forge/compiler/classifier/secrets.ts +41 -0
- package/src/forge/compiler/classifier/signals.ts +129 -0
- package/src/forge/compiler/client-sdk/build-manifest.ts +151 -0
- package/src/forge/compiler/client-sdk/render-client.ts +432 -0
- package/src/forge/compiler/data-graph/build.ts +131 -0
- package/src/forge/compiler/data-graph/constants.ts +5 -0
- package/src/forge/compiler/data-graph/index.ts +6 -0
- package/src/forge/compiler/data-graph/parse.ts +176 -0
- package/src/forge/compiler/data-graph/rls/build.ts +222 -0
- package/src/forge/compiler/data-graph/rls/types.ts +62 -0
- package/src/forge/compiler/data-graph/sql/ddl.ts +390 -0
- package/src/forge/compiler/data-graph/sql/naming.ts +10 -0
- package/src/forge/compiler/data-graph/sql/serialize.ts +85 -0
- package/src/forge/compiler/data-graph/sql/types.ts +37 -0
- package/src/forge/compiler/dev-manifest/build.ts +170 -0
- package/src/forge/compiler/dev-manifest/constants.ts +5 -0
- package/src/forge/compiler/diagnostics/codes.ts +611 -0
- package/src/forge/compiler/diagnostics/create.ts +245 -0
- package/src/forge/compiler/diagnostics/index.ts +55 -0
- package/src/forge/compiler/emitter/artifact-kind.ts +14 -0
- package/src/forge/compiler/emitter/barrel.ts +44 -0
- package/src/forge/compiler/emitter/constants.ts +7 -0
- package/src/forge/compiler/emitter/emit.ts +237 -0
- package/src/forge/compiler/emitter/index.ts +24 -0
- package/src/forge/compiler/emitter/lock.ts +62 -0
- package/src/forge/compiler/emitter/render.ts +73 -0
- package/src/forge/compiler/emitter/write.ts +35 -0
- package/src/forge/compiler/frontend-graph/build.ts +495 -0
- package/src/forge/compiler/fs/index.ts +23 -0
- package/src/forge/compiler/fs/memory.ts +233 -0
- package/src/forge/compiler/fs/node.ts +139 -0
- package/src/forge/compiler/fs/profile.ts +108 -0
- package/src/forge/compiler/fs/types.ts +52 -0
- package/src/forge/compiler/guards/artifacts.ts +96 -0
- package/src/forge/compiler/guards/check-ai-usage.ts +98 -0
- package/src/forge/compiler/guards/check-import-guards.ts +106 -0
- package/src/forge/compiler/guards/check-process-env.ts +98 -0
- package/src/forge/compiler/guards/check-query-usage.ts +76 -0
- package/src/forge/compiler/guards/index.ts +11 -0
- package/src/forge/compiler/guards/propagate-contexts.ts +57 -0
- package/src/forge/compiler/index.ts +17 -0
- package/src/forge/compiler/integration/add.ts +496 -0
- package/src/forge/compiler/integration/index.ts +17 -0
- package/src/forge/compiler/integration/plan.ts +283 -0
- package/src/forge/compiler/integration/render.ts +189 -0
- package/src/forge/compiler/integration/snapshot.ts +52 -0
- package/src/forge/compiler/integration/templates/ai.ts +131 -0
- package/src/forge/compiler/integration/templates/index.ts +8 -0
- package/src/forge/compiler/integration/templates/posthog.ts +145 -0
- package/src/forge/compiler/integration/templates/render.ts +113 -0
- package/src/forge/compiler/integration/templates/sentry.ts +151 -0
- package/src/forge/compiler/integration/templates/stripe.ts +109 -0
- package/src/forge/compiler/integration/templates/types.ts +14 -0
- package/src/forge/compiler/integration/templates/zod.ts +55 -0
- package/src/forge/compiler/live-production/types.ts +122 -0
- package/src/forge/compiler/live-query-registry/build.ts +150 -0
- package/src/forge/compiler/live-query-registry/constants.ts +2 -0
- package/src/forge/compiler/make-registry/build.ts +179 -0
- package/src/forge/compiler/orchestrator/discover.ts +214 -0
- package/src/forge/compiler/orchestrator/fast-check.ts +117 -0
- package/src/forge/compiler/orchestrator/generate-lock.ts +138 -0
- package/src/forge/compiler/orchestrator/guards.ts +5 -0
- package/src/forge/compiler/orchestrator/index.ts +27 -0
- package/src/forge/compiler/orchestrator/manifest-hashes.ts +21 -0
- package/src/forge/compiler/orchestrator/manifest.ts +92 -0
- package/src/forge/compiler/orchestrator/orphans.ts +51 -0
- package/src/forge/compiler/orchestrator/plan.ts +876 -0
- package/src/forge/compiler/orchestrator/profile.ts +36 -0
- package/src/forge/compiler/orchestrator/run.ts +277 -0
- package/src/forge/compiler/orchestrator/serialize.ts +886 -0
- package/src/forge/compiler/orchestrator/session.ts +96 -0
- package/src/forge/compiler/orchestrator/types.ts +31 -0
- package/src/forge/compiler/orchestrator/verify.ts +38 -0
- package/src/forge/compiler/orchestrator/workspace-index.ts +154 -0
- package/src/forge/compiler/package-graph/capabilities-stub.ts +33 -0
- package/src/forge/compiler/package-graph/checksum.ts +97 -0
- package/src/forge/compiler/package-graph/compiler.ts +392 -0
- package/src/forge/compiler/package-graph/constants.ts +4 -0
- package/src/forge/compiler/package-graph/dts-extractor.ts +142 -0
- package/src/forge/compiler/package-graph/exports-discovery.ts +84 -0
- package/src/forge/compiler/package-graph/extract-dts.ts +32 -0
- package/src/forge/compiler/package-graph/index.ts +33 -0
- package/src/forge/compiler/package-graph/jsdoc.ts +62 -0
- package/src/forge/compiler/package-graph/read-file.ts +21 -0
- package/src/forge/compiler/package-graph/resolve.ts +127 -0
- package/src/forge/compiler/package-manager/adapter.ts +237 -0
- package/src/forge/compiler/package-manager/bun-executable.ts +92 -0
- package/src/forge/compiler/package-manager/commands.ts +47 -0
- package/src/forge/compiler/package-manager/detect.ts +79 -0
- package/src/forge/compiler/package-manager/executor.ts +117 -0
- package/src/forge/compiler/package-manager/index.ts +22 -0
- package/src/forge/compiler/package-manager/parse-spec.ts +16 -0
- package/src/forge/compiler/package-manager/version.ts +27 -0
- package/src/forge/compiler/package-upgrades/apply.ts +195 -0
- package/src/forge/compiler/package-upgrades/comparator.ts +181 -0
- package/src/forge/compiler/package-upgrades/impact.ts +139 -0
- package/src/forge/compiler/package-upgrades/markdown.ts +97 -0
- package/src/forge/compiler/package-upgrades/planner.ts +532 -0
- package/src/forge/compiler/package-upgrades/risk.ts +208 -0
- package/src/forge/compiler/package-upgrades/types.ts +174 -0
- package/src/forge/compiler/policy-registry/build.ts +266 -0
- package/src/forge/compiler/policy-registry/constants.ts +2 -0
- package/src/forge/compiler/policy-registry/parse.ts +81 -0
- package/src/forge/compiler/primitives/compare.ts +26 -0
- package/src/forge/compiler/primitives/hash.ts +40 -0
- package/src/forge/compiler/primitives/header.ts +45 -0
- package/src/forge/compiler/primitives/index.ts +45 -0
- package/src/forge/compiler/primitives/paths.ts +24 -0
- package/src/forge/compiler/primitives/result.ts +164 -0
- package/src/forge/compiler/primitives/serialize.ts +66 -0
- package/src/forge/compiler/primitives/sort.ts +87 -0
- package/src/forge/compiler/query-registry/build.ts +114 -0
- package/src/forge/compiler/query-registry/constants.ts +2 -0
- package/src/forge/compiler/recipes/definitions.ts +289 -0
- package/src/forge/compiler/recipes/helpers.ts +37 -0
- package/src/forge/compiler/recipes/index.ts +21 -0
- package/src/forge/compiler/recipes/registry.ts +102 -0
- package/src/forge/compiler/release/build.ts +100 -0
- package/src/forge/compiler/release/types.ts +119 -0
- package/src/forge/compiler/runtime-graph/build.ts +137 -0
- package/src/forge/compiler/runtime-graph/constants.ts +5 -0
- package/src/forge/compiler/runtime-graph/index.ts +5 -0
- package/src/forge/compiler/sandbox/artifact-sanitize.ts +26 -0
- package/src/forge/compiler/sandbox/backends/child.ts +123 -0
- package/src/forge/compiler/sandbox/backends/docker.ts +173 -0
- package/src/forge/compiler/sandbox/index.ts +51 -0
- package/src/forge/compiler/sandbox/inspect.ts +143 -0
- package/src/forge/compiler/sandbox/inspector-entry.ts +115 -0
- package/src/forge/compiler/sandbox/limits.ts +31 -0
- package/src/forge/compiler/sandbox/scrub-env.ts +60 -0
- package/src/forge/compiler/sandbox/secret-scan.ts +54 -0
- package/src/forge/compiler/sandbox/serialize.ts +106 -0
- package/src/forge/compiler/sandbox/types.ts +7 -0
- package/src/forge/compiler/secret-registry/build.ts +123 -0
- package/src/forge/compiler/telemetry-registry/build.ts +89 -0
- package/src/forge/compiler/telemetry-registry/constants.ts +2 -0
- package/src/forge/compiler/telemetry-registry/parse.ts +13 -0
- package/src/forge/compiler/test-graph/build.ts +277 -0
- package/src/forge/compiler/types/action-subscriptions.ts +19 -0
- package/src/forge/compiler/types/ai-registry.ts +33 -0
- package/src/forge/compiler/types/app-graph.ts +80 -0
- package/src/forge/compiler/types/capability.ts +29 -0
- package/src/forge/compiler/types/classification.ts +9 -0
- package/src/forge/compiler/types/cli.ts +159 -0
- package/src/forge/compiler/types/data-graph.ts +24 -0
- package/src/forge/compiler/types/dev-manifest.ts +41 -0
- package/src/forge/compiler/types/diagnostic.ts +12 -0
- package/src/forge/compiler/types/emit.ts +25 -0
- package/src/forge/compiler/types/frontend-graph.ts +81 -0
- package/src/forge/compiler/types/import-guards.ts +19 -0
- package/src/forge/compiler/types/index.ts +98 -0
- package/src/forge/compiler/types/integration.ts +25 -0
- package/src/forge/compiler/types/json.ts +3 -0
- package/src/forge/compiler/types/live-query-registry.ts +32 -0
- package/src/forge/compiler/types/lock.ts +37 -0
- package/src/forge/compiler/types/package-graph.ts +84 -0
- package/src/forge/compiler/types/policy-registry.ts +69 -0
- package/src/forge/compiler/types/query-registry.ts +18 -0
- package/src/forge/compiler/types/runtime-graph.ts +30 -0
- package/src/forge/compiler/types/runtime-matrix.ts +16 -0
- package/src/forge/compiler/types/runtime.ts +30 -0
- package/src/forge/compiler/types/sandbox.ts +24 -0
- package/src/forge/compiler/types/secret-registry.ts +38 -0
- package/src/forge/compiler/types/telemetry-registry.ts +26 -0
- package/src/forge/compiler/types/test-graph.ts +45 -0
- package/src/forge/compiler/types/workflow-registry.ts +42 -0
- package/src/forge/compiler/workflow-registry/build.ts +180 -0
- package/src/forge/compiler/workflow-registry/constants.ts +2 -0
- package/src/forge/compiler/workflow-registry/index.ts +5 -0
- package/src/forge/compiler/workflow-registry/parse.ts +19 -0
- package/src/forge/dev/server.ts +1379 -0
- package/src/forge/dev/types.ts +49 -0
- package/src/forge/dev/watch.ts +109 -0
- package/src/forge/dev-console/cycle.ts +652 -0
- package/src/forge/dev-console/types.ts +99 -0
- package/src/forge/feature/compiler.ts +656 -0
- package/src/forge/feature/examples.ts +125 -0
- package/src/forge/feature/types.ts +177 -0
- package/src/forge/impact/index.ts +1160 -0
- package/src/forge/impact/types.ts +151 -0
- package/src/forge/intent/index.ts +490 -0
- package/src/forge/intent/types.ts +73 -0
- package/src/forge/make/fields.ts +146 -0
- package/src/forge/make/index.ts +1101 -0
- package/src/forge/make/naming.ts +42 -0
- package/src/forge/make/templates.ts +525 -0
- package/src/forge/make/types.ts +151 -0
- package/src/forge/platform/module.ts +20 -0
- package/src/forge/policy.ts +1 -0
- package/src/forge/react/index.ts +418 -0
- package/src/forge/refactor/index.ts +1936 -0
- package/src/forge/refactor/text-utils.ts +34 -0
- package/src/forge/refactor/types.ts +191 -0
- package/src/forge/refactor/workspace-fs.ts +171 -0
- package/src/forge/repair/index.ts +656 -0
- package/src/forge/repair/rules/index.ts +476 -0
- package/src/forge/repair/types.ts +175 -0
- package/src/forge/review/index.ts +992 -0
- package/src/forge/review/types.ts +196 -0
- package/src/forge/runtime/ai/check.ts +86 -0
- package/src/forge/runtime/ai/context.ts +394 -0
- package/src/forge/runtime/ai/cost-estimator.ts +41 -0
- package/src/forge/runtime/ai/mock.ts +49 -0
- package/src/forge/runtime/ai/providers.ts +78 -0
- package/src/forge/runtime/ai/state.ts +17 -0
- package/src/forge/runtime/ai/types.ts +67 -0
- package/src/forge/runtime/auth/authenticate.ts +58 -0
- package/src/forge/runtime/auth/claims.ts +119 -0
- package/src/forge/runtime/auth/config.ts +148 -0
- package/src/forge/runtime/auth/errors.ts +45 -0
- package/src/forge/runtime/auth/evaluate.ts +126 -0
- package/src/forge/runtime/auth/resolve.ts +74 -0
- package/src/forge/runtime/auth/types.ts +87 -0
- package/src/forge/runtime/auth/verifier.ts +138 -0
- package/src/forge/runtime/context/create-context.ts +204 -0
- package/src/forge/runtime/context/create-query-context.ts +34 -0
- package/src/forge/runtime/db/adapter.ts +31 -0
- package/src/forge/runtime/db/factory.ts +83 -0
- package/src/forge/runtime/db/generated-client.ts +294 -0
- package/src/forge/runtime/db/memory-adapter.ts +706 -0
- package/src/forge/runtime/db/migrate.ts +132 -0
- package/src/forge/runtime/db/outbox.ts +54 -0
- package/src/forge/runtime/db/pglite-adapter.ts +51 -0
- package/src/forge/runtime/db/postgres-adapter.ts +112 -0
- package/src/forge/runtime/db/read-only-client.ts +97 -0
- package/src/forge/runtime/db/session-context.ts +62 -0
- package/src/forge/runtime/executor.ts +446 -0
- package/src/forge/runtime/live/dependency-tracker.ts +57 -0
- package/src/forge/runtime/live/invalidation-log.ts +189 -0
- package/src/forge/runtime/live/live-query-runner.ts +267 -0
- package/src/forge/runtime/live/registry.ts +28 -0
- package/src/forge/runtime/live/sse.ts +75 -0
- package/src/forge/runtime/live/subscription-manager.ts +443 -0
- package/src/forge/runtime/live/types.ts +143 -0
- package/src/forge/runtime/outbox/claim.ts +153 -0
- package/src/forge/runtime/outbox/process.ts +298 -0
- package/src/forge/runtime/outbox/retry.ts +8 -0
- package/src/forge/runtime/outbox/subscriptions.ts +33 -0
- package/src/forge/runtime/outbox/types.ts +69 -0
- package/src/forge/runtime/policy/check.ts +157 -0
- package/src/forge/runtime/policy/load.ts +55 -0
- package/src/forge/runtime/query/registry.ts +19 -0
- package/src/forge/runtime/query/run-query.ts +347 -0
- package/src/forge/runtime/release/runtime.ts +322 -0
- package/src/forge/runtime/release/symbolicate.ts +175 -0
- package/src/forge/runtime/runner/command-transaction.ts +193 -0
- package/src/forge/runtime/runner/run-entry.ts +226 -0
- package/src/forge/runtime/secrets/check.ts +78 -0
- package/src/forge/runtime/secrets/create-context.ts +138 -0
- package/src/forge/runtime/secrets/env-loader.ts +94 -0
- package/src/forge/runtime/secrets/runtime-bundle.ts +47 -0
- package/src/forge/runtime/secrets/types.ts +31 -0
- package/src/forge/runtime/telemetry/buffer.ts +87 -0
- package/src/forge/runtime/telemetry/context.ts +192 -0
- package/src/forge/runtime/telemetry/correlation.ts +13 -0
- package/src/forge/runtime/telemetry/flush.ts +190 -0
- package/src/forge/runtime/telemetry/process.ts +20 -0
- package/src/forge/runtime/telemetry/scrubber.ts +115 -0
- package/src/forge/runtime/telemetry/sinks/local-jsonl.ts +39 -0
- package/src/forge/runtime/telemetry/sinks/posthog.ts +64 -0
- package/src/forge/runtime/telemetry/sinks/sentry.ts +60 -0
- package/src/forge/runtime/telemetry/spans.ts +58 -0
- package/src/forge/runtime/telemetry/types.ts +64 -0
- package/src/forge/runtime/workflows/cancel.ts +26 -0
- package/src/forge/runtime/workflows/create-run.ts +98 -0
- package/src/forge/runtime/workflows/process-run.ts +182 -0
- package/src/forge/runtime/workflows/process-step.ts +190 -0
- package/src/forge/runtime/workflows/process.ts +260 -0
- package/src/forge/runtime/workflows/registry.ts +51 -0
- package/src/forge/runtime/workflows/resolve-step.ts +46 -0
- package/src/forge/runtime/workflows/retry-run.ts +44 -0
- package/src/forge/runtime/workflows/retry.ts +8 -0
- package/src/forge/runtime/workflows/sanitize.ts +19 -0
- package/src/forge/runtime/workflows/start-from-outbox.ts +71 -0
- package/src/forge/runtime/workflows/types.ts +77 -0
- package/src/forge/server.ts +96 -0
- package/src/forge/ui/index.ts +770 -0
- package/src/forge/ui/types.ts +191 -0
- package/templates/b2b-support-web/.env.example +22 -0
- package/templates/b2b-support-web/.vscode/settings.json +14 -0
- package/templates/b2b-support-web/AGENTS.md +108 -0
- package/templates/b2b-support-web/README.md +48 -0
- package/templates/b2b-support-web/forge.config.ts +3 -0
- package/templates/b2b-support-web/package.json +34 -0
- package/templates/b2b-support-web/src/actions/captureTicketCreated.ts +14 -0
- package/templates/b2b-support-web/src/commands/closeTicket.ts +20 -0
- package/templates/b2b-support-web/src/commands/createTicket.ts +47 -0
- package/templates/b2b-support-web/src/commands/manageBilling.ts +9 -0
- package/templates/b2b-support-web/src/forge/schema.ts +35 -0
- package/templates/b2b-support-web/src/policies.ts +9 -0
- package/templates/b2b-support-web/src/queries/getTicket.ts +6 -0
- package/templates/b2b-support-web/src/queries/listTickets.ts +6 -0
- package/templates/b2b-support-web/src/queries/liveTickets.ts +9 -0
- package/templates/b2b-support-web/src/workflows/triageTicketWorkflow.ts +64 -0
- package/templates/b2b-support-web/tsconfig.json +14 -0
- package/templates/b2b-support-web/web/app/globals.css +77 -0
- package/templates/b2b-support-web/web/app/layout.tsx +13 -0
- package/templates/b2b-support-web/web/app/page.tsx +13 -0
- package/templates/b2b-support-web/web/app/providers.tsx +21 -0
- package/templates/b2b-support-web/web/app/tickets/page.tsx +21 -0
- package/templates/b2b-support-web/web/components/CreateTicketForm.tsx +43 -0
- package/templates/b2b-support-web/web/components/PolicyDeniedDemo.tsx +31 -0
- package/templates/b2b-support-web/web/components/TicketList.tsx +52 -0
- package/templates/b2b-support-web/web/components/TraceDetails.tsx +18 -0
- package/templates/b2b-support-web/web/components/TriageStatus.tsx +13 -0
- package/templates/b2b-support-web/web/lib/forge.ts +13 -0
- package/templates/b2b-support-web/web/next-env.d.ts +5 -0
- package/templates/b2b-support-web/web/next.config.ts +8 -0
- package/templates/b2b-support-web/web/package.json +21 -0
- package/templates/b2b-support-web/web/tsconfig.json +30 -0
- package/templates/minimal-web/.vscode/settings.json +14 -0
- package/templates/minimal-web/README.md +21 -0
- package/templates/minimal-web/forge.config.ts +3 -0
- package/templates/minimal-web/package.json +32 -0
- package/templates/minimal-web/src/actions/logNoteCreated.ts +11 -0
- package/templates/minimal-web/src/commands/createNote.ts +26 -0
- package/templates/minimal-web/src/forge/schema.ts +12 -0
- package/templates/minimal-web/src/policies.ts +6 -0
- package/templates/minimal-web/src/queries/listNotes.ts +8 -0
- package/templates/minimal-web/src/queries/liveNotes.ts +8 -0
- package/templates/minimal-web/tsconfig.json +15 -0
- package/templates/minimal-web/web/index.html +12 -0
- package/templates/minimal-web/web/package.json +21 -0
- package/templates/minimal-web/web/src/App.tsx +89 -0
- package/templates/minimal-web/web/src/lib/forge.ts +13 -0
- package/templates/minimal-web/web/src/main.tsx +13 -0
- package/templates/minimal-web/web/src/styles.css +156 -0
- package/templates/minimal-web/web/tsconfig.json +18 -0
|
@@ -0,0 +1,1002 @@
|
|
|
1
|
+
import { nodeFileSystem } from "../compiler/fs/index.ts";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { createDiagnostic } from "../compiler/diagnostics/create.ts";
|
|
4
|
+
import { GENERATED_DIR, GENERATOR_VERSION } from "../compiler/emitter/constants.ts";
|
|
5
|
+
import { stripDeterministicHeader } from "../compiler/primitives/header.ts";
|
|
6
|
+
import { hashStable } from "../compiler/primitives/hash.ts";
|
|
7
|
+
import { serializeCanonical } from "../compiler/primitives/serialize.ts";
|
|
8
|
+
import { secretLeakScan } from "../compiler/sandbox/secret-scan.ts";
|
|
9
|
+
import type { Diagnostic } from "../compiler/types/diagnostic.ts";
|
|
10
|
+
import type { AgentContract } from "../compiler/agent-contract/types.ts";
|
|
11
|
+
import {
|
|
12
|
+
FORGE_AGENT_ADAPTER_INVALID,
|
|
13
|
+
FORGE_AGENT_EXPORT_FAILED,
|
|
14
|
+
FORGE_AGENT_MARKERS_MISSING,
|
|
15
|
+
FORGE_AGENT_SECRET_LEAK,
|
|
16
|
+
FORGE_AGENT_STALE_EXPORT,
|
|
17
|
+
FORGE_AGENT_TARGET_UNKNOWN,
|
|
18
|
+
FORGE_AGENT_TEMPLATE_RENDER_FAILED,
|
|
19
|
+
} from "../compiler/diagnostics/codes.ts";
|
|
20
|
+
import type {
|
|
21
|
+
AgentAdapterManifest,
|
|
22
|
+
AgentAdapterTarget,
|
|
23
|
+
AgentCommandOptions,
|
|
24
|
+
AgentCommandsMap,
|
|
25
|
+
AgentContext,
|
|
26
|
+
AgentDoneCriteria,
|
|
27
|
+
AgentDoctorResult,
|
|
28
|
+
AgentExportFile,
|
|
29
|
+
AgentExportResult,
|
|
30
|
+
AgentPrintContextResult,
|
|
31
|
+
AgentTargetsResult,
|
|
32
|
+
CustomAdapterConfig,
|
|
33
|
+
AgentCheckResult,
|
|
34
|
+
} from "./types.ts";
|
|
35
|
+
|
|
36
|
+
export const AGENT_ADAPTER_VERSION = "agent-adapter-0.1.0";
|
|
37
|
+
export const AGENT_FORMAT_VERSION = "2026-06";
|
|
38
|
+
|
|
39
|
+
const USER_START = "<!-- user-notes:start -->";
|
|
40
|
+
const USER_END = "<!-- user-notes:end -->";
|
|
41
|
+
const GENERATED_START = "<!-- forge-generated:start -->";
|
|
42
|
+
const GENERATED_END = "<!-- forge-generated:end -->";
|
|
43
|
+
const CUSTOM_ADAPTERS_DIR = ".forge/agent-adapters";
|
|
44
|
+
|
|
45
|
+
function sorted(values: string[]): string[] {
|
|
46
|
+
return [...new Set(values.filter(Boolean))].sort();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function diagnostic(
|
|
50
|
+
severity: Diagnostic["severity"],
|
|
51
|
+
code: string,
|
|
52
|
+
message: string,
|
|
53
|
+
file?: string,
|
|
54
|
+
): Diagnostic {
|
|
55
|
+
return createDiagnostic({ severity, code, message, ...(file ? { file } : {}) });
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readText(workspaceRoot: string, relative: string): string | null {
|
|
59
|
+
const path = join(workspaceRoot, relative);
|
|
60
|
+
if (!nodeFileSystem.exists(path)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
return stripDeterministicHeader((nodeFileSystem.readText(path) ?? ""));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readJson<T>(workspaceRoot: string, relative: string): T | null {
|
|
67
|
+
const text = readText(workspaceRoot, relative);
|
|
68
|
+
if (text === null) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
return JSON.parse(text) as T;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function writeText(workspaceRoot: string, relative: string, content: string): void {
|
|
75
|
+
const path = join(workspaceRoot, relative);
|
|
76
|
+
nodeFileSystem.mkdirp(dirname(path));
|
|
77
|
+
nodeFileSystem.writeText(path, content);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function renderJson(value: unknown): string {
|
|
81
|
+
return serializeCanonical(value);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function tsExport(name: string, value: unknown): string {
|
|
85
|
+
const parsed = JSON.parse(renderJson(value).trimEnd()) as unknown;
|
|
86
|
+
return `export const ${name} = ${JSON.stringify(parsed, null, 2)} as const;\n`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function sourceHash(contract: AgentContract): string {
|
|
90
|
+
return `sha256:${hashStable(renderJson(contract))}`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function buildAgentAdapterManifest(contract: AgentContract): AgentAdapterManifest {
|
|
94
|
+
const genericFiles = buildGenericFiles(contract).map((file) => file.path);
|
|
95
|
+
return {
|
|
96
|
+
schemaVersion: "0.1.0",
|
|
97
|
+
generatorVersion: GENERATOR_VERSION,
|
|
98
|
+
sourceHash: sourceHash(contract),
|
|
99
|
+
targets: [
|
|
100
|
+
{
|
|
101
|
+
name: "generic",
|
|
102
|
+
default: true,
|
|
103
|
+
adapterVersion: AGENT_ADAPTER_VERSION,
|
|
104
|
+
formatVersion: AGENT_FORMAT_VERSION,
|
|
105
|
+
files: genericFiles,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "codex",
|
|
109
|
+
optional: true,
|
|
110
|
+
adapterVersion: AGENT_ADAPTER_VERSION,
|
|
111
|
+
formatVersion: AGENT_FORMAT_VERSION,
|
|
112
|
+
files: buildCodexFiles(contract, { skills: true }).map((file) => file.path),
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
name: "cursor",
|
|
116
|
+
optional: true,
|
|
117
|
+
adapterVersion: AGENT_ADAPTER_VERSION,
|
|
118
|
+
formatVersion: AGENT_FORMAT_VERSION,
|
|
119
|
+
files: buildCursorFiles(contract, { rules: true }).map((file) => file.path),
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: "claude",
|
|
123
|
+
optional: true,
|
|
124
|
+
adapterVersion: AGENT_ADAPTER_VERSION,
|
|
125
|
+
formatVersion: AGENT_FORMAT_VERSION,
|
|
126
|
+
files: buildClaudeFiles(contract).map((file) => file.path),
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function serializeAgentAdapterManifestJson(manifest: AgentAdapterManifest): string {
|
|
133
|
+
return renderJson(manifest);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function serializeAgentAdapterManifestTs(manifest: AgentAdapterManifest): string {
|
|
137
|
+
return tsExport("agentAdapterManifest", manifest);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function buildAgentContext(contract: AgentContract): AgentContext {
|
|
141
|
+
return {
|
|
142
|
+
schemaVersion: "0.1.0",
|
|
143
|
+
project: {
|
|
144
|
+
name: contract.project.name,
|
|
145
|
+
framework: "forgeos",
|
|
146
|
+
...(contract.project.template ? { template: contract.project.template } : {}),
|
|
147
|
+
},
|
|
148
|
+
runtimeModel: {
|
|
149
|
+
command: "transactional write, no network/secrets/AI",
|
|
150
|
+
query: "read-only, tenant-scoped",
|
|
151
|
+
liveQuery: "read-only reactive query",
|
|
152
|
+
action: "side effects after commit",
|
|
153
|
+
workflow: "durable steps after outbox event",
|
|
154
|
+
},
|
|
155
|
+
commands: sorted(contract.commands.map((entry) => entry.name)),
|
|
156
|
+
queries: sorted(contract.queries.map((entry) => entry.name)),
|
|
157
|
+
liveQueries: sorted(contract.liveQueries.map((entry) => entry.name)),
|
|
158
|
+
actions: sorted(contract.actions.map((entry) => entry.name)),
|
|
159
|
+
workflows: sorted(contract.workflows.map((entry) => entry.name)),
|
|
160
|
+
tables: sorted(contract.data.tables.map((entry) => entry.name)),
|
|
161
|
+
policies: sorted(contract.policies.map((entry) => entry.name)),
|
|
162
|
+
secrets: sorted(contract.secrets.map((entry) => entry.name)),
|
|
163
|
+
criticalCommands: {
|
|
164
|
+
afterSourceChange: ["forge generate", "forge check"],
|
|
165
|
+
beforeCommit: ["forge verify --strict"],
|
|
166
|
+
targetedLoop: [
|
|
167
|
+
"forge impact --changed --json",
|
|
168
|
+
"forge test plan --changed --json",
|
|
169
|
+
"forge test run --changed --json",
|
|
170
|
+
],
|
|
171
|
+
repair: ["forge repair diagnose --from-last-test-run --json"],
|
|
172
|
+
},
|
|
173
|
+
knownPitfalls: [
|
|
174
|
+
"Do not edit src/forge/_generated/** directly.",
|
|
175
|
+
"Do not use process.env directly in app code.",
|
|
176
|
+
"Do not import network packages in command/query/liveQuery.",
|
|
177
|
+
"Preserve tenant isolation and policy declarations.",
|
|
178
|
+
"Use forge make, forge feature, forge refactor, forge impact, and forge repair before hand-editing architecture.",
|
|
179
|
+
],
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function buildAgentCommandsMap(): AgentCommandsMap {
|
|
184
|
+
return {
|
|
185
|
+
setup: ["bun install"],
|
|
186
|
+
dev: ["forge dev"],
|
|
187
|
+
generate: ["forge generate"],
|
|
188
|
+
check: ["forge check"],
|
|
189
|
+
verify: ["forge verify --strict"],
|
|
190
|
+
impact: ["forge impact --changed --json"],
|
|
191
|
+
testPlan: ["forge test plan --changed --json"],
|
|
192
|
+
testRun: ["forge test run --changed --json"],
|
|
193
|
+
repair: ["forge repair diagnose --from-last-test-run --json"],
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function buildAgentDoneCriteria(): AgentDoneCriteria {
|
|
198
|
+
return {
|
|
199
|
+
default: [
|
|
200
|
+
"forge generate --check passes",
|
|
201
|
+
"forge check passes",
|
|
202
|
+
"forge verify --strict passes",
|
|
203
|
+
"no edits under src/forge/_generated",
|
|
204
|
+
"no direct process.env in app code",
|
|
205
|
+
"no forbidden runtime imports",
|
|
206
|
+
],
|
|
207
|
+
frontendChange: [
|
|
208
|
+
"affected React tests pass",
|
|
209
|
+
"client entrypoints remain client-safe",
|
|
210
|
+
"liveQuery smoke passes if live data changed",
|
|
211
|
+
],
|
|
212
|
+
schemaChange: [
|
|
213
|
+
"forge db diff reviewed",
|
|
214
|
+
"RLS check passes for tenant-scoped tables",
|
|
215
|
+
"affected queries/liveQueries tested",
|
|
216
|
+
],
|
|
217
|
+
packageChange: [
|
|
218
|
+
"forge deps upgrade-plan reviewed",
|
|
219
|
+
"runtimeMatrix unchanged or expected",
|
|
220
|
+
"affected integration tests pass",
|
|
221
|
+
],
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function replaceGeneratedBlock(existing: string | null, generated: string, fallbackUserNotes: string): string {
|
|
226
|
+
const userBlock = extractUserBlock(existing) ?? `${USER_START}\n\n${fallbackUserNotes}\n\n${USER_END}`;
|
|
227
|
+
return `${GENERATED_START}\n${generated.trim()}\n${GENERATED_END}\n\n${userBlock.trim()}\n`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function extractUserBlock(existing: string | null): string | null {
|
|
231
|
+
if (!existing) {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
const start = existing.indexOf(USER_START);
|
|
235
|
+
const end = existing.indexOf(USER_END);
|
|
236
|
+
if (start === -1 || end === -1 || end < start) {
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
return existing.slice(start, end + USER_END.length);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function agentsMarkdown(contract: AgentContract, existing: string | null): string {
|
|
243
|
+
const context = buildAgentContext(contract);
|
|
244
|
+
const generated = `# AGENTS.md
|
|
245
|
+
|
|
246
|
+
## Project Type
|
|
247
|
+
|
|
248
|
+
This is a ForgeOS application.
|
|
249
|
+
|
|
250
|
+
## Required Workflow
|
|
251
|
+
|
|
252
|
+
Before editing:
|
|
253
|
+
|
|
254
|
+
\`\`\`bash
|
|
255
|
+
forge inspect all --json
|
|
256
|
+
forge doctor --json
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
During editing:
|
|
260
|
+
|
|
261
|
+
\`\`\`bash
|
|
262
|
+
forge impact --changed --json
|
|
263
|
+
forge test plan --changed --json
|
|
264
|
+
\`\`\`
|
|
265
|
+
|
|
266
|
+
After editing:
|
|
267
|
+
|
|
268
|
+
\`\`\`bash
|
|
269
|
+
forge generate
|
|
270
|
+
forge check
|
|
271
|
+
forge verify --strict
|
|
272
|
+
\`\`\`
|
|
273
|
+
|
|
274
|
+
## Do Not
|
|
275
|
+
|
|
276
|
+
- Do not edit \`src/forge/_generated/**\`.
|
|
277
|
+
- Do not import network packages in \`command\`, \`query\`, or \`liveQuery\`.
|
|
278
|
+
- Do not use \`process.env\` directly in app code.
|
|
279
|
+
- Use \`ctx.secrets\`.
|
|
280
|
+
- Do not bypass tenant isolation.
|
|
281
|
+
- Do not call \`ctx.ai\` in \`command\`, \`query\`, or \`liveQuery\`.
|
|
282
|
+
- Do not manually modify \`forge.lock\` unless instructed.
|
|
283
|
+
|
|
284
|
+
## Runtime Model
|
|
285
|
+
|
|
286
|
+
- \`command\`: ${context.runtimeModel.command}.
|
|
287
|
+
- \`query\`: ${context.runtimeModel.query}.
|
|
288
|
+
- \`liveQuery\`: ${context.runtimeModel.liveQuery}.
|
|
289
|
+
- \`action\`: ${context.runtimeModel.action}.
|
|
290
|
+
- \`workflow\`: ${context.runtimeModel.workflow}.
|
|
291
|
+
|
|
292
|
+
## Common Commands
|
|
293
|
+
|
|
294
|
+
\`\`\`bash
|
|
295
|
+
forge make resource <name>
|
|
296
|
+
forge feature plan <blueprint>
|
|
297
|
+
forge refactor rename field <from> <to>
|
|
298
|
+
forge impact --changed --json
|
|
299
|
+
forge repair diagnose --from-last-test-run --json
|
|
300
|
+
forge agent print-context --json
|
|
301
|
+
\`\`\`
|
|
302
|
+
|
|
303
|
+
## Agent Adapter Exports
|
|
304
|
+
|
|
305
|
+
- Generic agents read \`.forge/agent/context.json\` and \`.forge/agent/playbooks/*.md\`.
|
|
306
|
+
- Codex skills are generated under \`.codex/skills/**\`.
|
|
307
|
+
- Cursor rules are generated under \`.cursor/rules/**\`.
|
|
308
|
+
- Claude instructions are generated in \`CLAUDE.md\` and \`.claude/**\`.
|
|
309
|
+
|
|
310
|
+
These files are derived from ForgeOS generated contracts. Regenerate them with:
|
|
311
|
+
|
|
312
|
+
\`\`\`bash
|
|
313
|
+
forge agent export --target all
|
|
314
|
+
\`\`\``;
|
|
315
|
+
return `# AGENTS.md\n\n${replaceGeneratedBlock(existing, generated, "Project-specific human notes go here.")}`;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function playbook(title: string, steps: string[]): string {
|
|
319
|
+
return `# Playbook: ${title}\n\n${steps.map((step, index) => `${index + 1}. ${step}`).join("\n")}\n`;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function playbookFiles(): AgentExportFile[] {
|
|
323
|
+
const books: Array<[string, string, string[]]> = [
|
|
324
|
+
["add-command.md", "Add Command", [
|
|
325
|
+
"Run `forge inspect all --json`.",
|
|
326
|
+
"Prefer `forge make command <resource.action> --table <table> --policy <policy>`.",
|
|
327
|
+
"Commands may write through `ctx.db` and emit with `ctx.emit`.",
|
|
328
|
+
"Commands must not import network packages, use `ctx.secrets`, or call `ctx.ai`.",
|
|
329
|
+
"Run `forge generate`, `forge check`, and `forge verify --changed`.",
|
|
330
|
+
"Finish with `forge verify --strict`.",
|
|
331
|
+
]],
|
|
332
|
+
["add-query.md", "Add Query", [
|
|
333
|
+
"Run `forge inspect data --json` and `forge inspect policies --json`.",
|
|
334
|
+
"Prefer `forge make query <name> --table <table> --policy <policy>`.",
|
|
335
|
+
"Keep queries read-only and tenant-scoped.",
|
|
336
|
+
"Run `forge generate` and `forge check`.",
|
|
337
|
+
]],
|
|
338
|
+
["add-livequery.md", "Add LiveQuery", [
|
|
339
|
+
"Prefer `forge make livequery <name> --table <table> --policy <policy>`.",
|
|
340
|
+
"Keep liveQueries read-only and reactive.",
|
|
341
|
+
"Run `forge live status --json` when debugging subscriptions.",
|
|
342
|
+
"Run `forge verify --changed`.",
|
|
343
|
+
]],
|
|
344
|
+
["add-resource.md", "Add Resource", [
|
|
345
|
+
"Run `forge make resource <name> --fields name:string --dry-run --json`.",
|
|
346
|
+
"Review planned schema, policies, commands, queries, and components.",
|
|
347
|
+
"Apply with `forge make resource <name> --fields ... --yes`.",
|
|
348
|
+
"Run `forge generate` and `forge verify --strict`.",
|
|
349
|
+
]],
|
|
350
|
+
["refactor-field.md", "Refactor Field", [
|
|
351
|
+
"Use `forge refactor rename field <from> <to> --plan` before manual edits.",
|
|
352
|
+
"Inspect the plan and public API risk.",
|
|
353
|
+
"Apply with `forge refactor apply <planId> --yes`.",
|
|
354
|
+
"Run `forge impact --changed --json` and targeted tests.",
|
|
355
|
+
]],
|
|
356
|
+
["fix-policy-denied.md", "Fix Policy Denied", [
|
|
357
|
+
"Run `forge repair diagnose --from-last-test-run --json`.",
|
|
358
|
+
"Run `forge policy simulate <policy> --role <role>`.",
|
|
359
|
+
"Prefer policy changes through `forge make policy`.",
|
|
360
|
+
"Run `forge verify --strict` after changing access rules.",
|
|
361
|
+
]],
|
|
362
|
+
["fix-guard-violation.md", "Fix FORGE_GUARD_VIOLATION", [
|
|
363
|
+
"Run `forge repair diagnose --from-last-test-run --json`.",
|
|
364
|
+
"If a network package is reachable from command/query/liveQuery, prefer `forge refactor extract-action <command> --package <package> --event <event>`.",
|
|
365
|
+
"Run `forge generate`, `forge check`, and `forge verify --changed`.",
|
|
366
|
+
"Finish with `forge verify --strict`.",
|
|
367
|
+
]],
|
|
368
|
+
["upgrade-package.md", "Upgrade Package", [
|
|
369
|
+
"Run `forge deps upgrade-plan <package> --to latest --json`.",
|
|
370
|
+
"Review runtime context, secret, and API risk.",
|
|
371
|
+
"Apply only after reviewing the plan.",
|
|
372
|
+
"Run impacted tests and `forge verify --strict`.",
|
|
373
|
+
]],
|
|
374
|
+
["debug-trace.md", "Debug Trace", [
|
|
375
|
+
"Capture the `traceId` from frontend or runtime output.",
|
|
376
|
+
"Run `forge telemetry inspect <traceId>`.",
|
|
377
|
+
"Run `forge repair diagnose --trace <traceId> --json`.",
|
|
378
|
+
"Prefer targeted repairs and impacted tests before full verify.",
|
|
379
|
+
]],
|
|
380
|
+
["frontend-change.md", "Frontend Change", [
|
|
381
|
+
"Use generated client APIs and React hooks.",
|
|
382
|
+
"Do not import server adapters or server-only packages into client code.",
|
|
383
|
+
"Preserve `ForgeError.traceId` in visible error states.",
|
|
384
|
+
"Run affected frontend tests and `forge verify --changed`.",
|
|
385
|
+
]],
|
|
386
|
+
["self-host-check.md", "Self-host Check", [
|
|
387
|
+
"Run `forge self-host check`.",
|
|
388
|
+
"Run `forge release artifacts verify` when deployment artifacts changed.",
|
|
389
|
+
"Run `forge verify --strict` before handoff.",
|
|
390
|
+
]],
|
|
391
|
+
];
|
|
392
|
+
return books.map(([name, title, steps]) => ({
|
|
393
|
+
path: `.forge/agent/playbooks/${name}`,
|
|
394
|
+
content: playbook(title, steps),
|
|
395
|
+
}));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function buildGenericFiles(contract: AgentContract, existingAgentsMd?: string | null): AgentExportFile[] {
|
|
399
|
+
return [
|
|
400
|
+
{ path: "AGENTS.md", content: existingAgentsMd ?? agentsMarkdown(contract, null) },
|
|
401
|
+
{ path: ".forge/agent/context.json", content: renderJson(buildAgentContext(contract)) },
|
|
402
|
+
{ path: ".forge/agent/commands.json", content: renderJson(buildAgentCommandsMap()) },
|
|
403
|
+
{ path: ".forge/agent/done-criteria.json", content: renderJson(buildAgentDoneCriteria()) },
|
|
404
|
+
...playbookFiles(),
|
|
405
|
+
];
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function skill(name: string, description: string, body: string): string {
|
|
409
|
+
return `---\nname: ${name}\ndescription: ${description}\n---\n\n# ${description}\n\n${body.trim()}\n`;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function buildCodexFiles(_contract: AgentContract, options: { skills: boolean }): AgentExportFile[] {
|
|
413
|
+
if (!options.skills) {
|
|
414
|
+
return [];
|
|
415
|
+
}
|
|
416
|
+
const skills: Array<[string, string, string]> = [
|
|
417
|
+
["forge-add-command", "Use when adding or modifying a ForgeOS command.", `
|
|
418
|
+
Rules:
|
|
419
|
+
- Commands require \`auth: can("...")\`.
|
|
420
|
+
- Commands may write through \`ctx.db\`.
|
|
421
|
+
- Commands may call \`ctx.emit\`.
|
|
422
|
+
- Commands must not import network packages.
|
|
423
|
+
- Commands must not use \`ctx.secrets\` or \`ctx.ai\`.
|
|
424
|
+
|
|
425
|
+
Steps:
|
|
426
|
+
1. Run \`forge inspect all --json\`.
|
|
427
|
+
2. Prefer \`forge make command <resource.action> --table <table> --policy <policy>\`.
|
|
428
|
+
3. Run \`forge generate\`, \`forge check\`, and \`forge verify --changed\`.
|
|
429
|
+
4. Finish with \`forge verify --strict\`.
|
|
430
|
+
`],
|
|
431
|
+
["forge-add-resource", "Use when adding a ForgeOS resource.", `
|
|
432
|
+
Use \`forge make resource <name> --fields ... --dry-run --json\` first.
|
|
433
|
+
Review generated table, policy, command, query, liveQuery, component, and page changes.
|
|
434
|
+
Apply with \`--yes\`, then run \`forge generate\` and \`forge verify --strict\`.
|
|
435
|
+
`],
|
|
436
|
+
["forge-fix-guard-violation", "Use when ForgeOS reports FORGE_GUARD_VIOLATION.", `
|
|
437
|
+
Run \`forge repair diagnose --from-last-test-run --json\`.
|
|
438
|
+
If a network package is reachable from command/query/liveQuery, prefer:
|
|
439
|
+
\`forge refactor extract-action <command> --package <package> --event <event>\`.
|
|
440
|
+
`],
|
|
441
|
+
["forge-fix-policy-denied", "Use when ForgeOS reports a policy or auth denial.", `
|
|
442
|
+
Run \`forge repair diagnose --from-last-test-run --json\` and \`forge policy simulate <policy> --role <role>\`.
|
|
443
|
+
Prefer changing policies through \`forge make policy\`.
|
|
444
|
+
`],
|
|
445
|
+
["forge-upgrade-package", "Use when upgrading a package in a ForgeOS app.", `
|
|
446
|
+
Run \`forge deps upgrade-plan <package> --to latest --json\`.
|
|
447
|
+
Review runtime, secret, and API risks before applying.
|
|
448
|
+
Run impacted tests and \`forge verify --strict\`.
|
|
449
|
+
`],
|
|
450
|
+
["forge-debug-trace", "Use when debugging a ForgeOS trace or failing runtime operation.", `
|
|
451
|
+
Run \`forge telemetry inspect <traceId>\`.
|
|
452
|
+
Then run \`forge repair diagnose --trace <traceId> --json\`.
|
|
453
|
+
Prefer targeted checks before full verification.
|
|
454
|
+
`],
|
|
455
|
+
];
|
|
456
|
+
const files = skills.map(([name, description, body]) => ({
|
|
457
|
+
path: `.codex/skills/${name}/SKILL.md`,
|
|
458
|
+
content: skill(name, description, body),
|
|
459
|
+
}));
|
|
460
|
+
const agents = ["explorer", "worker", "reviewer", "security"].map((name) => ({
|
|
461
|
+
path: `.codex/agents/forge-${name}.toml`,
|
|
462
|
+
content: `name = "forge-${name}"\ndescription = "ForgeOS ${name} helper generated from agentAdapterManifest."\n`,
|
|
463
|
+
}));
|
|
464
|
+
return [...files, ...agents];
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
function mdc(description: string, globs: string[], body: string): string {
|
|
468
|
+
return `---\ndescription: ${description}\nglobs:\n${globs.map((glob) => ` - "${glob}"`).join("\n")}\nalwaysApply: true\n---\n\n${body.trim()}\n`;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function buildCursorFiles(_contract: AgentContract, options: { rules: boolean }): AgentExportFile[] {
|
|
472
|
+
if (!options.rules) {
|
|
473
|
+
return [];
|
|
474
|
+
}
|
|
475
|
+
return [
|
|
476
|
+
{
|
|
477
|
+
path: ".cursor/rules/forge-runtime.mdc",
|
|
478
|
+
content: mdc(
|
|
479
|
+
"ForgeOS runtime rules for commands, queries, liveQueries, actions and workflows.",
|
|
480
|
+
["src/**/*.ts", "src/**/*.tsx"],
|
|
481
|
+
`# ForgeOS Runtime Rules
|
|
482
|
+
|
|
483
|
+
- Do not edit \`src/forge/_generated/**\`.
|
|
484
|
+
- Commands are transactional writes.
|
|
485
|
+
- Queries and liveQueries are read-only.
|
|
486
|
+
- Commands must not import network packages.
|
|
487
|
+
- Commands must not use \`ctx.secrets\` or \`ctx.ai\`.
|
|
488
|
+
- Use \`ctx.emit\` for side effects.
|
|
489
|
+
- Use actions/workflows for external APIs.
|
|
490
|
+
- Run \`forge check\` after changing runtime code.`,
|
|
491
|
+
),
|
|
492
|
+
},
|
|
493
|
+
{
|
|
494
|
+
path: ".cursor/rules/forge-frontend.mdc",
|
|
495
|
+
content: mdc(
|
|
496
|
+
"ForgeOS frontend/client rules.",
|
|
497
|
+
["web/**/*.tsx", "web/**/*.ts", "src/**/*.tsx"],
|
|
498
|
+
`# ForgeOS Frontend Rules
|
|
499
|
+
|
|
500
|
+
- Import generated API and hooks.
|
|
501
|
+
- Do not import server adapters or server-only packages.
|
|
502
|
+
- Use \`useLiveQuery\`, \`useQuery\`, and \`useCommand\`.
|
|
503
|
+
- Preserve \`ForgeError.traceId\` in user-visible error states.`,
|
|
504
|
+
),
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
path: ".cursor/rules/forge-security.mdc",
|
|
508
|
+
content: mdc(
|
|
509
|
+
"ForgeOS tenant, policy, and secret safety rules.",
|
|
510
|
+
["src/**/*.ts", "web/**/*.ts", "web/**/*.tsx"],
|
|
511
|
+
`# ForgeOS Security Rules
|
|
512
|
+
|
|
513
|
+
- Never include secret values in generated files, logs, or adapter exports.
|
|
514
|
+
- Use \`ctx.secrets\`; do not read \`process.env\` directly in app code.
|
|
515
|
+
- Preserve tenant-scoped reads and writes.
|
|
516
|
+
- Run \`forge policy check --strict-policies\` after access changes.`,
|
|
517
|
+
),
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
path: ".cursor/rules/forge-workflow.mdc",
|
|
521
|
+
content: mdc(
|
|
522
|
+
"ForgeOS workflow, impact, repair, and verification workflow.",
|
|
523
|
+
["src/**/*.ts", "tests/**/*.ts"],
|
|
524
|
+
`# ForgeOS Workflow
|
|
525
|
+
|
|
526
|
+
- Prefer \`forge make\` for new primitives.
|
|
527
|
+
- Prefer \`forge feature\` for blueprint-driven changes.
|
|
528
|
+
- Prefer \`forge refactor\` for renames, moves, and side-effect extraction.
|
|
529
|
+
- Run \`forge impact --changed --json\` and \`forge test plan --changed --json\`.
|
|
530
|
+
- Use \`forge repair diagnose --from-last-test-run --json\` after failures.`,
|
|
531
|
+
),
|
|
532
|
+
},
|
|
533
|
+
];
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function buildClaudeFiles(_contract: AgentContract): AgentExportFile[] {
|
|
537
|
+
const claude = `# CLAUDE.md
|
|
538
|
+
|
|
539
|
+
This is a ForgeOS app.
|
|
540
|
+
|
|
541
|
+
Start with:
|
|
542
|
+
|
|
543
|
+
\`\`\`bash
|
|
544
|
+
forge inspect all --json
|
|
545
|
+
forge doctor --json
|
|
546
|
+
\`\`\`
|
|
547
|
+
|
|
548
|
+
After changes:
|
|
549
|
+
|
|
550
|
+
\`\`\`bash
|
|
551
|
+
forge generate
|
|
552
|
+
forge verify --strict
|
|
553
|
+
\`\`\`
|
|
554
|
+
|
|
555
|
+
Critical rules:
|
|
556
|
+
|
|
557
|
+
- Do not edit \`src/forge/_generated/**\`.
|
|
558
|
+
- Commands cannot use network packages, secrets, or AI.
|
|
559
|
+
- Queries/liveQueries are read-only.
|
|
560
|
+
- Use \`ctx.emit\` for side effects.
|
|
561
|
+
- Use \`ctx.secrets\`, never \`process.env\`.
|
|
562
|
+
`;
|
|
563
|
+
return [
|
|
564
|
+
{ path: "CLAUDE.md", content: claude },
|
|
565
|
+
{ path: ".claude/forge-runtime.md", content: readmeRuntime() },
|
|
566
|
+
{ path: ".claude/forge-playbooks.md", content: playbookFiles().map((file) => file.content).join("\n") },
|
|
567
|
+
{ path: ".claude/forge-security.md", content: "# ForgeOS Security\n\nNever include secret values. Preserve policies, tenant scopes, and generated runtime rules.\n" },
|
|
568
|
+
];
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function readmeRuntime(): string {
|
|
572
|
+
return `# ForgeOS Runtime
|
|
573
|
+
|
|
574
|
+
- Commands are transactional writes and emit events for side effects.
|
|
575
|
+
- Queries and liveQueries are read-only.
|
|
576
|
+
- Actions and workflows perform side effects after commit.
|
|
577
|
+
- AI is allowed in actions/workflows/endpoints/server, not commands/queries/liveQueries.
|
|
578
|
+
`;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function loadCustomAdapter(workspaceRoot: string, target: string): CustomAdapterConfig | null {
|
|
582
|
+
return readJson<CustomAdapterConfig>(workspaceRoot, `${CUSTOM_ADAPTERS_DIR}/${target}/adapter.json`);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function getPath(value: unknown, path: string): unknown {
|
|
586
|
+
return path.split(".").reduce<unknown>((current, part) => {
|
|
587
|
+
if (typeof current === "object" && current !== null && part in current) {
|
|
588
|
+
return (current as Record<string, unknown>)[part];
|
|
589
|
+
}
|
|
590
|
+
return undefined;
|
|
591
|
+
}, value);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
function renderTemplate(template: string, context: Record<string, unknown>): string {
|
|
595
|
+
return template.replace(/\{\{\s*([^}]+?)\s*\}\}/g, (_match, expression: string) => {
|
|
596
|
+
const trimmed = expression.trim();
|
|
597
|
+
if (trimmed.startsWith("json ")) {
|
|
598
|
+
const value = getPath(context, trimmed.slice(5).trim());
|
|
599
|
+
return JSON.stringify(value, null, 2);
|
|
600
|
+
}
|
|
601
|
+
const value = getPath(context, trimmed);
|
|
602
|
+
if (value === undefined || value === null) {
|
|
603
|
+
return "";
|
|
604
|
+
}
|
|
605
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function buildCustomFiles(workspaceRoot: string, target: string, contract: AgentContract): { files: AgentExportFile[]; diagnostics: Diagnostic[] } {
|
|
610
|
+
const config = loadCustomAdapter(workspaceRoot, target);
|
|
611
|
+
if (!config) {
|
|
612
|
+
return {
|
|
613
|
+
files: [],
|
|
614
|
+
diagnostics: [
|
|
615
|
+
diagnostic(
|
|
616
|
+
"error",
|
|
617
|
+
FORGE_AGENT_TARGET_UNKNOWN,
|
|
618
|
+
`unknown agent adapter target: ${target}`,
|
|
619
|
+
),
|
|
620
|
+
],
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
if (!Array.isArray(config.outputs)) {
|
|
624
|
+
return {
|
|
625
|
+
files: [],
|
|
626
|
+
diagnostics: [
|
|
627
|
+
diagnostic(
|
|
628
|
+
"error",
|
|
629
|
+
FORGE_AGENT_ADAPTER_INVALID,
|
|
630
|
+
`invalid custom adapter config: ${CUSTOM_ADAPTERS_DIR}/${target}/adapter.json`,
|
|
631
|
+
),
|
|
632
|
+
],
|
|
633
|
+
};
|
|
634
|
+
}
|
|
635
|
+
const context = {
|
|
636
|
+
agentContract: contract,
|
|
637
|
+
context: buildAgentContext(contract),
|
|
638
|
+
commands: buildAgentCommandsMap(),
|
|
639
|
+
project: contract.project,
|
|
640
|
+
};
|
|
641
|
+
const files: AgentExportFile[] = [];
|
|
642
|
+
const diagnostics: Diagnostic[] = [];
|
|
643
|
+
for (const output of config.outputs) {
|
|
644
|
+
const templatePath = `${CUSTOM_ADAPTERS_DIR}/${target}/templates/${output.template}`;
|
|
645
|
+
const template = readText(workspaceRoot, templatePath);
|
|
646
|
+
if (template === null) {
|
|
647
|
+
diagnostics.push(
|
|
648
|
+
diagnostic(
|
|
649
|
+
"error",
|
|
650
|
+
FORGE_AGENT_TEMPLATE_RENDER_FAILED,
|
|
651
|
+
`custom adapter template not found: ${templatePath}`,
|
|
652
|
+
templatePath,
|
|
653
|
+
),
|
|
654
|
+
);
|
|
655
|
+
continue;
|
|
656
|
+
}
|
|
657
|
+
files.push({
|
|
658
|
+
path: output.path,
|
|
659
|
+
content: renderTemplate(template, context),
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
return { files, diagnostics };
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function builtFilesForTarget(
|
|
666
|
+
workspaceRoot: string,
|
|
667
|
+
contract: AgentContract,
|
|
668
|
+
target: AgentAdapterTarget,
|
|
669
|
+
options: Pick<AgentCommandOptions, "skills" | "rules">,
|
|
670
|
+
): { files: AgentExportFile[]; diagnostics: Diagnostic[] } {
|
|
671
|
+
const existingAgentsMd = readText(workspaceRoot, "AGENTS.md");
|
|
672
|
+
if (target === "generic") {
|
|
673
|
+
return { files: buildGenericFiles(contract, existingAgentsMd), diagnostics: [] };
|
|
674
|
+
}
|
|
675
|
+
if (target === "codex") {
|
|
676
|
+
return { files: [...buildGenericFiles(contract, existingAgentsMd), ...buildCodexFiles(contract, { skills: options.skills })], diagnostics: [] };
|
|
677
|
+
}
|
|
678
|
+
if (target === "cursor") {
|
|
679
|
+
return { files: [...buildGenericFiles(contract, existingAgentsMd), ...buildCursorFiles(contract, { rules: options.rules })], diagnostics: [] };
|
|
680
|
+
}
|
|
681
|
+
if (target === "claude") {
|
|
682
|
+
return { files: [...buildGenericFiles(contract, existingAgentsMd), ...buildClaudeFiles(contract)], diagnostics: [] };
|
|
683
|
+
}
|
|
684
|
+
if (target === "all") {
|
|
685
|
+
const byPath = new Map<string, AgentExportFile>();
|
|
686
|
+
for (const file of [
|
|
687
|
+
...buildGenericFiles(contract, existingAgentsMd),
|
|
688
|
+
...buildCodexFiles(contract, { skills: options.skills }),
|
|
689
|
+
...buildCursorFiles(contract, { rules: options.rules }),
|
|
690
|
+
...buildClaudeFiles(contract),
|
|
691
|
+
]) {
|
|
692
|
+
byPath.set(file.path, file);
|
|
693
|
+
}
|
|
694
|
+
return { files: [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path)), diagnostics: [] };
|
|
695
|
+
}
|
|
696
|
+
return buildCustomFiles(workspaceRoot, target, contract);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function validateFiles(files: AgentExportFile[]): Diagnostic[] {
|
|
700
|
+
const diagnostics: Diagnostic[] = [];
|
|
701
|
+
for (const file of files) {
|
|
702
|
+
const scan = secretLeakScan(file.content, { includeHighEntropy: false });
|
|
703
|
+
if (scan.hasLeak) {
|
|
704
|
+
diagnostics.push(
|
|
705
|
+
diagnostic(
|
|
706
|
+
"error",
|
|
707
|
+
FORGE_AGENT_SECRET_LEAK,
|
|
708
|
+
`agent adapter output may contain a secret: ${scan.matches.join(", ")}`,
|
|
709
|
+
file.path,
|
|
710
|
+
),
|
|
711
|
+
);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
return diagnostics;
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function loadContract(workspaceRoot: string): { contract: AgentContract | null; diagnostics: Diagnostic[] } {
|
|
718
|
+
const contract = readJson<AgentContract>(workspaceRoot, `${GENERATED_DIR}/agentContract.json`);
|
|
719
|
+
if (!contract) {
|
|
720
|
+
return {
|
|
721
|
+
contract: null,
|
|
722
|
+
diagnostics: [
|
|
723
|
+
diagnostic(
|
|
724
|
+
"error",
|
|
725
|
+
FORGE_AGENT_EXPORT_FAILED,
|
|
726
|
+
`missing ${GENERATED_DIR}/agentContract.json; run forge generate first`,
|
|
727
|
+
`${GENERATED_DIR}/agentContract.json`,
|
|
728
|
+
),
|
|
729
|
+
],
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
return { contract, diagnostics: [] };
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
export function runAgentExport(options: AgentCommandOptions): AgentExportResult {
|
|
736
|
+
const { contract, diagnostics } = loadContract(options.workspaceRoot);
|
|
737
|
+
if (!contract) {
|
|
738
|
+
return {
|
|
739
|
+
ok: false,
|
|
740
|
+
target: options.target,
|
|
741
|
+
filesWritten: [],
|
|
742
|
+
filesPlanned: [],
|
|
743
|
+
warnings: [],
|
|
744
|
+
diagnostics,
|
|
745
|
+
exitCode: 1,
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
const built = builtFilesForTarget(options.workspaceRoot, contract, options.target, options);
|
|
749
|
+
const validation = validateFiles(built.files);
|
|
750
|
+
const allDiagnostics = [...built.diagnostics, ...validation];
|
|
751
|
+
if (allDiagnostics.some((diag) => diag.severity === "error")) {
|
|
752
|
+
return {
|
|
753
|
+
ok: false,
|
|
754
|
+
target: options.target,
|
|
755
|
+
filesWritten: [],
|
|
756
|
+
filesPlanned: built.files.map((file) => file.path),
|
|
757
|
+
warnings: allDiagnostics.filter((diag) => diag.severity === "warning"),
|
|
758
|
+
diagnostics: allDiagnostics,
|
|
759
|
+
exitCode: 1,
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
if (options.dryRun) {
|
|
763
|
+
return {
|
|
764
|
+
ok: true,
|
|
765
|
+
target: options.target,
|
|
766
|
+
filesWritten: [],
|
|
767
|
+
filesPlanned: built.files.map((file) => file.path),
|
|
768
|
+
warnings: [],
|
|
769
|
+
diagnostics: [],
|
|
770
|
+
exitCode: 0,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
const filesWritten: string[] = [];
|
|
774
|
+
for (const file of built.files) {
|
|
775
|
+
const existing = readText(options.workspaceRoot, file.path);
|
|
776
|
+
if (
|
|
777
|
+
options.preserveUserSections &&
|
|
778
|
+
existing &&
|
|
779
|
+
file.path.endsWith(".md") &&
|
|
780
|
+
existing.includes(USER_START) !== existing.includes(USER_END)
|
|
781
|
+
) {
|
|
782
|
+
return {
|
|
783
|
+
ok: false,
|
|
784
|
+
target: options.target,
|
|
785
|
+
filesWritten,
|
|
786
|
+
filesPlanned: built.files.map((candidate) => candidate.path),
|
|
787
|
+
warnings: [],
|
|
788
|
+
diagnostics: [
|
|
789
|
+
diagnostic(
|
|
790
|
+
"error",
|
|
791
|
+
FORGE_AGENT_MARKERS_MISSING,
|
|
792
|
+
`malformed user section markers in ${file.path}`,
|
|
793
|
+
file.path,
|
|
794
|
+
),
|
|
795
|
+
],
|
|
796
|
+
exitCode: 1,
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
if (existing !== file.content || options.force) {
|
|
800
|
+
writeText(options.workspaceRoot, file.path, file.content);
|
|
801
|
+
filesWritten.push(file.path);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return {
|
|
805
|
+
ok: true,
|
|
806
|
+
target: options.target,
|
|
807
|
+
filesWritten,
|
|
808
|
+
filesPlanned: built.files.map((file) => file.path),
|
|
809
|
+
warnings: [],
|
|
810
|
+
diagnostics: [],
|
|
811
|
+
exitCode: 0,
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
export function runAgentCheck(options: AgentCommandOptions): AgentCheckResult {
|
|
816
|
+
const { contract, diagnostics } = loadContract(options.workspaceRoot);
|
|
817
|
+
if (!contract) {
|
|
818
|
+
return { ok: false, stale: [], missing: [], warnings: [], diagnostics, exitCode: 1 };
|
|
819
|
+
}
|
|
820
|
+
const built = builtFilesForTarget(options.workspaceRoot, contract, options.target, options);
|
|
821
|
+
const validation = validateFiles(built.files);
|
|
822
|
+
const stale: string[] = [];
|
|
823
|
+
const missing: string[] = [];
|
|
824
|
+
for (const file of built.files) {
|
|
825
|
+
const existing = readText(options.workspaceRoot, file.path);
|
|
826
|
+
if (existing === null) {
|
|
827
|
+
missing.push(file.path);
|
|
828
|
+
} else if (existing !== file.content) {
|
|
829
|
+
stale.push(file.path);
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
const diag = [...built.diagnostics, ...validation];
|
|
833
|
+
for (const file of stale) {
|
|
834
|
+
diag.push(diagnostic("error", FORGE_AGENT_STALE_EXPORT, `stale agent adapter export: ${file}`, file));
|
|
835
|
+
}
|
|
836
|
+
for (const file of missing) {
|
|
837
|
+
diag.push(diagnostic("error", FORGE_AGENT_STALE_EXPORT, `missing agent adapter export: ${file}`, file));
|
|
838
|
+
}
|
|
839
|
+
const ok = stale.length === 0 && missing.length === 0 && diag.every((item) => item.severity !== "error");
|
|
840
|
+
return {
|
|
841
|
+
ok,
|
|
842
|
+
stale,
|
|
843
|
+
missing,
|
|
844
|
+
warnings: diag.filter((item) => item.severity === "warning"),
|
|
845
|
+
diagnostics: diag,
|
|
846
|
+
exitCode: ok ? 0 : 1,
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
export function runAgentPrintContext(workspaceRoot: string): AgentPrintContextResult {
|
|
851
|
+
const { contract, diagnostics } = loadContract(workspaceRoot);
|
|
852
|
+
if (!contract) {
|
|
853
|
+
return { context: null, diagnostics, exitCode: 1 };
|
|
854
|
+
}
|
|
855
|
+
return { context: buildAgentContext(contract), diagnostics: [], exitCode: 0 };
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
export function listCustomTargets(workspaceRoot: string): string[] {
|
|
859
|
+
const dir = join(workspaceRoot, CUSTOM_ADAPTERS_DIR);
|
|
860
|
+
if (!nodeFileSystem.exists(dir)) {
|
|
861
|
+
return [];
|
|
862
|
+
}
|
|
863
|
+
return nodeFileSystem
|
|
864
|
+
.readDir(dir)
|
|
865
|
+
.map((entry) => entry.name)
|
|
866
|
+
.filter((entry) => nodeFileSystem.exists(join(dir, entry, "adapter.json")))
|
|
867
|
+
.sort();
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
export function runAgentListTargets(workspaceRoot: string): AgentTargetsResult {
|
|
871
|
+
return {
|
|
872
|
+
targets: [
|
|
873
|
+
{ name: "generic", default: true },
|
|
874
|
+
{ name: "codex", optional: true },
|
|
875
|
+
{ name: "cursor", optional: true },
|
|
876
|
+
{ name: "claude", optional: true },
|
|
877
|
+
...listCustomTargets(workspaceRoot).map((name) => ({ name, custom: true })),
|
|
878
|
+
],
|
|
879
|
+
exitCode: 0,
|
|
880
|
+
};
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
function targetFiles(target: AgentAdapterTarget): string[] {
|
|
884
|
+
if (target === "generic") {
|
|
885
|
+
return [".forge/agent"];
|
|
886
|
+
}
|
|
887
|
+
if (target === "codex") {
|
|
888
|
+
return [".codex"];
|
|
889
|
+
}
|
|
890
|
+
if (target === "cursor") {
|
|
891
|
+
return [".cursor"];
|
|
892
|
+
}
|
|
893
|
+
if (target === "claude") {
|
|
894
|
+
return ["CLAUDE.md", ".claude"];
|
|
895
|
+
}
|
|
896
|
+
if (target === "all") {
|
|
897
|
+
return [".forge/agent", ".codex", ".cursor", "CLAUDE.md", ".claude"];
|
|
898
|
+
}
|
|
899
|
+
return [];
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
export function runAgentClean(options: AgentCommandOptions): AgentExportResult {
|
|
903
|
+
const planned = targetFiles(options.target);
|
|
904
|
+
if (planned.length === 0) {
|
|
905
|
+
return {
|
|
906
|
+
ok: false,
|
|
907
|
+
target: options.target,
|
|
908
|
+
filesWritten: [],
|
|
909
|
+
filesPlanned: [],
|
|
910
|
+
warnings: [],
|
|
911
|
+
diagnostics: [
|
|
912
|
+
diagnostic("error", FORGE_AGENT_TARGET_UNKNOWN, `unknown clean target: ${options.target}`),
|
|
913
|
+
],
|
|
914
|
+
exitCode: 1,
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
if (!options.dryRun) {
|
|
918
|
+
for (const relative of planned) {
|
|
919
|
+
nodeFileSystem.remove(join(options.workspaceRoot, relative));
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
return {
|
|
923
|
+
ok: true,
|
|
924
|
+
target: options.target,
|
|
925
|
+
filesWritten: options.dryRun ? [] : planned,
|
|
926
|
+
filesPlanned: planned,
|
|
927
|
+
warnings: [],
|
|
928
|
+
diagnostics: [],
|
|
929
|
+
exitCode: 0,
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
export function runAgentDoctor(options: AgentCommandOptions): AgentDoctorResult {
|
|
934
|
+
const check = runAgentCheck(options);
|
|
935
|
+
const checks = [
|
|
936
|
+
{ name: "AGENTS.md", ok: !check.missing.includes("AGENTS.md") },
|
|
937
|
+
{ name: "agent-context", ok: !check.missing.includes(".forge/agent/context.json") },
|
|
938
|
+
{ name: "commands", ok: !check.missing.includes(".forge/agent/commands.json") },
|
|
939
|
+
{ name: "done-criteria", ok: !check.missing.includes(".forge/agent/done-criteria.json") },
|
|
940
|
+
{ name: "stale-exports", ok: check.stale.length === 0, message: check.stale.join(", ") || undefined },
|
|
941
|
+
{ name: "secret-scan", ok: !check.diagnostics.some((diag) => diag.code === FORGE_AGENT_SECRET_LEAK) },
|
|
942
|
+
];
|
|
943
|
+
const ok = checks.every((item) => item.ok) && check.exitCode === 0;
|
|
944
|
+
return { ok, checks, diagnostics: check.diagnostics, exitCode: ok ? 0 : 1 };
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
export async function runAgentCommand(options: AgentCommandOptions): Promise<
|
|
948
|
+
AgentExportResult | AgentCheckResult | AgentTargetsResult | AgentPrintContextResult | AgentDoctorResult
|
|
949
|
+
> {
|
|
950
|
+
if (options.subcommand === "list-targets") {
|
|
951
|
+
return runAgentListTargets(options.workspaceRoot);
|
|
952
|
+
}
|
|
953
|
+
if (options.subcommand === "export") {
|
|
954
|
+
return runAgentExport(options);
|
|
955
|
+
}
|
|
956
|
+
if (options.subcommand === "check") {
|
|
957
|
+
return runAgentCheck({ ...options, target: options.target || "generic" });
|
|
958
|
+
}
|
|
959
|
+
if (options.subcommand === "doctor") {
|
|
960
|
+
return runAgentDoctor({ ...options, target: options.target || "generic" });
|
|
961
|
+
}
|
|
962
|
+
if (options.subcommand === "print-context") {
|
|
963
|
+
return runAgentPrintContext(options.workspaceRoot);
|
|
964
|
+
}
|
|
965
|
+
if (options.subcommand === "clean") {
|
|
966
|
+
return runAgentClean(options);
|
|
967
|
+
}
|
|
968
|
+
return {
|
|
969
|
+
ok: false,
|
|
970
|
+
target: options.target,
|
|
971
|
+
filesWritten: [],
|
|
972
|
+
filesPlanned: [],
|
|
973
|
+
warnings: [],
|
|
974
|
+
diagnostics: [
|
|
975
|
+
diagnostic("error", FORGE_AGENT_EXPORT_FAILED, `unknown forge agent subcommand: ${options.subcommand}`),
|
|
976
|
+
],
|
|
977
|
+
exitCode: 1,
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
export function formatAgentJson(result: Awaited<ReturnType<typeof runAgentCommand>>): string {
|
|
982
|
+
return `${JSON.stringify(result, null, 2)}\n`;
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
export function formatAgentHuman(result: Awaited<ReturnType<typeof runAgentCommand>>): string {
|
|
986
|
+
if ("targets" in result) {
|
|
987
|
+
return `${result.targets.map((target) => `${target.name}${target.default ? " (default)" : ""}${target.optional ? " (optional)" : ""}${target.custom ? " (custom)" : ""}`).join("\n")}\n`;
|
|
988
|
+
}
|
|
989
|
+
if ("context" in result) {
|
|
990
|
+
return `${JSON.stringify(result.context, null, 2)}\n`;
|
|
991
|
+
}
|
|
992
|
+
if ("checks" in result) {
|
|
993
|
+
return `Forge Agent Doctor\n\n${result.checks.map((check) => `${check.ok ? "OK" : "WARN"} ${check.name}${check.message ? `: ${check.message}` : ""}`).join("\n")}\n`;
|
|
994
|
+
}
|
|
995
|
+
if ("stale" in result) {
|
|
996
|
+
if (result.ok) {
|
|
997
|
+
return "agent adapter exports are current\n";
|
|
998
|
+
}
|
|
999
|
+
return `agent adapter exports are stale\nmissing: ${result.missing.join(", ") || "none"}\nstale: ${result.stale.join(", ") || "none"}\n`;
|
|
1000
|
+
}
|
|
1001
|
+
return `agent export ${result.ok ? "ok" : "failed"} for ${result.target}\nfiles written:\n${result.filesWritten.map((file) => `- ${file}`).join("\n") || "- none"}\n`;
|
|
1002
|
+
}
|