add-env-variable
GitHub指导在任意服务包中添加配置变量,包括选择分组、定义类型、声明环境变量及元数据注解。适用于需要修改服务配置结构或添加新配置项的开发场景。
Trigger Scenarios
Install
npx skills add xinity-ai/xinity-ai --skill add-env-variable -g -y
SKILL.md
Frontmatter
{
"name": "add-env-variable",
"description": "Add a new configuration variable to any service package, declaring it on the package's grouped config declaration with an explicit env key, describe(), and meta(secret()) annotations."
}
Add Configuration Variable
Every service declares its configuration as named groups of fields. A variable is a field on one of those groups, bound to an env key that stays greppable.
Where it goes
| Package | Declaration | Resolved value |
|---|---|---|
| gateway | src/config-schema.ts -> gatewayConfig |
src/config.ts -> config |
| tether | src/config-schema.ts -> tetherConfig |
src/config.ts |
| daemon | src/config-schema.ts -> daemonConfig |
src/config.ts |
| infoserver | config-schema.ts -> infoserverConfig |
config.ts |
| dashboard | src/lib/server/config-schema.ts -> dashboardConfig |
src/lib/server/config.ts |
Steps
- Pick the group the variable belongs to, or add it top-level when it belongs to none.
Shared groups (
serverGroup,databaseGroup,catalogGroup,metricsGroup,objectStorageGroup,tlsGroup,proxyGroup,loggingGroup) already declare their own keys. - Add the field to the group's hand-written type first.
defineGroup<T>validates that the field keys matchkeyof Tand that each schema producesT[K], so the type is the contract and nothing is inferred. - Declare the field as
env("MY_NEW_VAR", schema). Both names appear in the source so the env key greps and the field jumps to its definition. - Use a dual leaf for non-strings:
configBool(),configInt(),configNumber(),configList(). Constraints go on theirbaseargument, as inconfigInt(z.int().positive()). - Mark secrets with
.meta(secret()). The CLI reads that to decide what goes into systemdLoadCredentialsecret files rather than plainEnvironmentFileentries. - Update
example.envif the variable wants a value for local dev.
Reading it is config.group.field. Nothing else needs changing: the CLI builds its editor from
the declaration, and the resolved value is typed from it.
Example
type WebSearch = { provider?: "searxng" | "google"; credential?: string };
const webSearch = defineGroup<WebSearch>({
id: "webSearch",
title: "Web search",
expert: true,
fields: {
provider: env("WEB_SEARCH_PROVIDER", z.enum(["searxng", "google"]).optional()
.describe("Web search backend. When unset, web search is disabled.")),
credential: env("WEB_SEARCH_CREDENTIAL", z.string().optional()
.describe("Provider credential").meta(secret())),
},
});
Groups that switch on and off
A group whose members are meaningless on their own takes optional: { requires: [...] }, naming
the fields whose presence activates it. It resolves to undefined when none are set, and warns at
boot when only some are. Every member has to be meaningless without those keys, so an optional
field inside an optional group is a sign the grouping is wrong.
Rules that span fields
violations: (value) => [{ field, message }] rejects combinations no single field can catch, such
as a keepalive longer than the idle timeout it has to fit inside. The CLI runs the same check
before it writes, so its editor cannot save a combination the service would refuse.
Version History
-
cd98a41
Current 2026-09-22 13:08
重构为基于分组的配置声明模式,替代原有的扁平env schema分离方式,支持更严格的类型检查和可选组逻辑。
- 9817b2f 2026-07-25 05:51


