一键导入
services
Scaffold and wire new services, packages, and frontends in a Forge project.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Scaffold and wire new services, packages, and frontends in a Forge project.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Visual-design discipline for forge frontends — brief the work before building (never invent an aesthetic without user input), declare the design system before coding, lean on the component library, use restrained color/type systems, never hand-draw complex SVGs, and verify visually before declaring done.
Write Next.js frontends — generated hooks, component library, Tailwind v4, visual verification, and Connect RPC clients.
Outbound boundary translators. One adapter per third-party system / queue / storage backend; narrow interface, vendor-neutral callers.
Write Connect RPC handlers — proto-driven codegen, the thin-translation handler pattern (validate, extract auth, convert proto↔internal, call service, wrap errors via `svcerr.Wrap`), middleware, and testing. Business logic lives in `internal/handlers/<svc>/contract.go`, never in handlers.
Forge project conventions and architecture — project structure, generated vs hand-written code, the generate pipeline, proto annotations, contracts, wiring, and naming.
Use-case orchestrators that compose two or more adapters/services. Deps are interfaces only — designed for unit tests with all-mock collaborators.
| name | services |
| description | Scaffold and wire new services, packages, and frontends in a Forge project. |
Use this skill whenever you need to introduce a new network-facing service, internal package, or frontend into a Forge mono-repo. Every scaffolded service comes with production middleware baked in — structured logging, distributed tracing, auth, rate limiting, and graceful shutdown — so you focus on business logic from day one.
| I need… | Command | What it creates |
|---|---|---|
| A new network-facing API (Connect RPC) | forge add service <name> | Proto definition, generated stubs, Go service skeleton |
| A background worker | forge add worker <name> | Worker with Start/Stop lifecycle |
| A cron-scheduled worker | forge add worker <name> --kind cron --schedule "..." | Worker with cron scheduler |
| An internal Go package with interface contract | forge package new <name> | Package directory with contract interface and default implementation |
| A Next.js web frontend | forge add frontend <name> | Next.js app wired into the project |
| A React Native mobile frontend | forge add frontend <name> --kind mobile | Expo app with Connect-web transport |
Follow this sequence every time you scaffold a new component:
forge add or forge package new command..proto file (services) or the interface (packages).forge generate to produce Go code from protos and contracts (handler stubs, mocks, Connect clients).Service interface in internal/handlers/<svc>/contract.go.NewComponents constructs it and the serve path mounts it. Add the constructor call to the composition (see below); selection is code, not a string table.Ports are assigned automatically via forge.yaml. Do not hard-code port numbers; let Forge manage them.
forge add or forge package new — never copy-paste an existing service or package directory.forge generate after any proto or contract change — generated code must stay in sync.forge add service admin-server keeps admin-server as the forge.yaml name: display key, but the on-disk leaf, Go package decl, and forge.yaml path: leaf are all admin_server (internal/handlers/admin_server/, package admin_server, path: internal/handlers/admin_server). See the workers skill Naming section for the full rule and the migration gotcha; see architecture for the cross-ecosystem naming-conventions table.internal/handlers/<svc>/ — contract.go, impl, and generated handlers co-located in ONE directory. The handlers/ role subtree is under internal/, not top-level; a service is app-internal, imported by nobody external.What a binary serves is the set of constructors it calls — not a string row in a registry. The explicit composition is split across two files under internal/app/: the owned providers.go (Infra + OpenInfra) and the generated compose.go (Components + NewComponents(infra *Infra) (*Components, error)). A binary serves a service because NewComponents constructs it and the serve path mounts its handler:
// internal/app/compose.go (forge-owned, regenerated — disown to hand-own)
func NewComponents(infra *Infra) (*Components, error) {
c := &Components{}
c.Users = user.New(user.Deps{Repo: infra.Repo})
c.Bill = billing.New(billing.Deps{Users: c.Users}) // collaborator by INTERFACE, in-process default
// two-phase (bill.WithReliantAPIKeyIssuer(infra.LLM)) → disown compose.go and edit here
return c, nil
}
The serve path (cmd/<bin>/cmd/serve.go) then applies the typed Mount<Svc> method values onto a serverkit.Server and calls srv.RequireMounted(...).
NewComponents and stop mounting it. To serve it elsewhere, construct it in that binary's composition. No forge.yaml edit, no string match.Users user.Service, never the concrete type — it can't tell whether it got the in-process instance, a Connect client, or a mock. Splitting a service into its own Deployment later is a one-line swap in NewComponents (billing.New(billing.Deps{Users: userclient.New(conn)})) with the consumer untouched.enforcement in both the server and the workspace-proxy) is one field on the owned Infra, opened once in OpenInfra and shared across the components.serverkit.Run takes a composed server with typed mounts, not service-name strings. It runs handlers + workers + operators + lifecycle; which of each is decided by what NewComponents constructed and the serve path mounted. There is no args/names variadic, no name-matched wiring.Each cobra subcommand is real cobra (own flags, own help, a jumpable Go symbol) — not a projection of a string table. cmd/billing.go is typically one line via a helper:
serverCmd("billing", app.MountBilling)
The server multi-mount subcommand composes all MountX. There is no generated cmd/services_gen.go and no server [services...] string-matching — both are gone. Custom non-service subcommands go in cmd/ as their own owned subcommand (see the binaries skill).
A data-only inventory ({Name, ConnectPath, Mount}) survives for forge map/forge audit and CLI listing. Names there are for display only — never a lookup key for construction. forge audit flags orphan stubs (a generated Unimplemented service nobody composes) and forge lint flags unresolved non-optional Deps and narrow-interface silent drops.
internal/ and write plain Go. No scaffold needed. (pkg/ is reserved for code with real external importers.)forge new <name> --mod <module> without --service to create a Cobra CLI binary with no server bootstrap.cmd/ (same image, opt-in config/OTel via the cmdkit paved path), or forge add binary <name> if it needs its own deploy lifecycle. A parallel cmd/<name>/main.go outside the cobra root is invisible to forge build/deploy — avoid it.