Bowrain Þŕöĵéçţ Ḿöđéļ
À bowrain þŕöĵéçţ îš à .kapi þŕöĵéçţ ŵîţĥ à bowrain: ƃļöçķ öñ îţš ŕéçîþé. Ţĥéŕé îš öñé þŕöĵéçţ ḿöđéļ šĥàŕéđ ŵîţĥ ţĥé kapi ÇĻÎ: à šîñĝļé kapi.yaml ŕéçîþé ƒîļé àţ ţĥé þŕöĵéçţ ŕööţ àñđ à šîƃļîñĝ .kapi/ đîŕéçţöŕý.
Đîŕéçţöŕý Šţŕüçţüŕé
my-app/
├── kapi.yaml # the recipe (committed); fixed, conventional filename
├── .kapi/ # committed; the project's context
│ ├── manifest.yaml # bookkeeping: block counts, fingerprints
│ ├── filters.json # shared reader/writer configuration
│ ├── filters.local.json # personal overrides (gitignored)
│ ├── flows/ # optional file-per-flow definitions
│ │ └── pseudo.yaml
│ ├── terms.json # terms (bound by defaults.terms_source)
│ ├── voice.yaml # the voice profile (bound by defaults.voice)
│ ├── memory/ # content-memory bundles
│ │ └── memory.json # the primary (bound by defaults.memory_source)
│ ├── profiles/ # per-profile governance overrides
│ │ └── bowrain/
│ │ └── voice.yaml
│ ├── state/ # the unit-state record, one shard per document
│ │ └── src-locales-en-messages.jsonl
│ └── work/ # gitignored; everything derived
│ ├── store.db # the local index over everything committed
│ ├── vault/ # withheld redaction originals (local-only)
│ └── cache/ # free to delete, always
│ ├── sync-cache.json # the tree last declared to the server
│ ├── extractions/
│ └── collections/
└── src/
└── locales/
├── en/
│ └── messages.json
└── fr/
└── messages.json
Öŵñéŕšĥîþ žöñéš àţ ţĥé þŕöĵéçţ ŕööţ:
kapi.yaml: ĥàñđ-éđîţéđ, çöḿḿîţţéđ ţö ĝîţ. Ţĥé ŕéçîþé îš ţĥé šîñĝļé šöüŕçé öƒ ţŕüţĥ ƒöŕ þŕöĵéçţ çöñƒîĝüŕàţîöñ. Îţš ƒîẋéđ, çöñṽéñţîöñàļ ƒîļéñàḿé ḿéàñš éṽéŕý éđîţöŕ àñđ çöđé ĥöšţ (ĜîţĤüƃ, ĜîţĻàƃ) àþþļîéš ÝÀḾĻ šýñţàẋ ĥîĝĥļîĝĥţîñĝ ţö đš àñđ þŕéṽîéŵš ŵîţĥ ñö çöñƒîĝüŕàţîöñ. Öñé ţĥîñĝ ŵŕîţéš îţ ƃéšîđéš ýöü: àñ àẋîš àþþŕöṽéđ öñ ţĥé šéŕṽéŕ àŕŕîṽéš àš àkapi pullţĥàţ éđîţšdefaults.coordinates, ƒöŕ ŕéṽîéŵ îñ ĝîţ..kapi/: ţĥé çöḿḿîţţéđ çöñţéẋţ ĝŕàþĥ, ƒļàţ:terms.json,memory/àñđvoice.yaml, ŵîţĥ þéŕ-þŕöƒîļé öṽéŕŕîđéš üñđéŕprofiles/<name>/, ŕéṽîéŵéđ ţĥŕöüĝĥgit diffļîķé àñý öţĥéŕ šöüŕçé ƒîļé..kapi/îš çöḿḿîţţéđ îñ ƒüļļ; öñļý.kapi/work/îš ĝîţîĝñöŕéđ..kapi/state/*.jsonl: ţĥé üñîţ-šţàţé ŕéçöŕđ, çöḿḿîţţéđ.kapi commitþüƃļîšĥéš šţàĝéđ üñîţ šţàţé îñţö îţ..kapi/work/store.db: kapi-öŵñéđ, ĝîţîĝñöŕéđ. Öñé ŠǪĻîţé ƒîļé ĥöļđîñĝ éṽéŕý šüƃšýšţéḿ'š ţàƃļéš: ƃļöçķ çàçĥé, ţéŕḿš šţöŕé, çöñţéñţ ḿéḿöŕý, ţĥé ŵöŕķîñĝ šéţ öƒ üñîţ šţàţé šţàĝéđ šîñçé ţĥé ļàšţkapi commit, àñđ ţĥé þŕöĵéçţ'š çöñţéẋţ ĝŕàþĥ. Îţ îš àñ îñđéẋ öṽéŕ ţĥé çöḿḿîţţéđ šöüŕçéš àƃöṽé àñđ ŕéƃüîļđš ƒŕöḿ ţĥéḿ..kapi/work/cache/: ÇĻÎ-öŵñéđ, ĝîţîĝñöŕéđ. Éṽéŕýţĥîñĝ çĥéàþļý ŕéĝéñéŕàƃļé: ţĥé ţŕéé ļàšţ đéçļàŕéđ ţö ţĥé šéŕṽéŕ, éẋţŕàçţîöñ îñţéŕḿéđîàţéš, öṽéŕļàý ļàýéŕš. Šàƒé ţö đéļéţé àţ àñý ţîḿé..kapi/flows/*.yaml: öþţîöñàļ ƒîļé-þéŕ-ƒļöŵ đéƒîñîţîöñš, ĥàñđ-éđîţéđ, çöḿḿîţţéđ. Bowrain ŕéàđš ţĥéšé îñ àđđîţîöñ ţö îñļîñéflows:đéçļàŕéđ öñ ţĥé ŕéçîþé.
Ļöçàļ àñđ šéŕṽéŕ çöñṽéŕĝé îñ šĥàþé. Bowrain àñšŵéŕš ĝŕàþĥ ǫüéšţîöñš öṽéŕ öñé đàţàƃàšé šþàññîñĝ ŵöŕķšþàçéš, þŕöĵéçţš àñđ šţŕéàḿš; à þŕöĵéçţ àñšŵéŕš ţĥé šàḿé ǫüéŕý šĥàþéš öṽéŕ store.db ŵîţĥ ţĥöšé đîḿéñšîöñš ƒîẋéđ ţö öñé ṽàļüé, šö ŵĥîçĥ ƃļöçķš üšé à ĝîṽéñ ţéŕḿ, ƃý çöļļéçţîöñ àñđ çööŕđîñàţé, îš àñšŵéŕàƃļé ŵîţĥ ñö šéŕṽéŕ.
Ŕéçîþé šçĥéḿà
Ţĥé ŕéçîþé îš à ÝÀḾĻ đöçüḿéñţ. Bowrain þŕöĵéçţš ļàýéŕ à bowrain: ƃļöçķ (àñđ ţĥé öþţîöñàļ ţöþ-ļéṽéļ ṽéñüé ķéýš hooks, automations, assets àñđ brand_voice) öñţö ţĥé ƒŕàḿéŵöŕķ'š KapiProject šçĥéḿà.
version: v1
name: My App
defaults:
source_language: en
target_languages: [fr, de, ja]
collection: ui/strings
exclude:
- "**/*.test.json"
- "node_modules/**"
terms_source: .kapi/terms.json
memory_source: .kapi/memory/memory.json
voice: .kapi/voice.yaml
coordinates:
brand: acme # a declared axis, inherited by every collection
collections:
- path: src/locales/**/*.json
format: json
- path: content/docs/**/*.md
format: markdown
target: i18n/{lang}/docs/{path}/{filename}
- name: legal
channel: docs
coordinates:
mode: reference # overrides the project on one axis, inherits the rest
source_only: true # checked, never translated
content:
- path: legal/**/*.md
format: markdown
plugins:
okapi-bridge: "^1.47.0" # map form: name → version constraint
flows:
pseudo:
steps:
- tool: pseudo-translate
config: { method: extended }
# The bowrain: block depends on the bowrain plugin. init declares the
# requirement so a plain kapi binary (without the plugin) fails fast instead of
# silently ignoring the connection.
requires:
bowrain: "*"
# Optional bowrain-server connection. Presence enables push/pull and makes the
# server the default venue for `kapi up`.
bowrain:
url: https://app.bowrain.cloud/my-team/abc123
stream: $auto # auto-detect from git branch / CI
converge: on-push # on-push (default) | manual
# Top-level lifecycle policy:
hooks:
pre-push: [qa]
post-pull: [segmentation]
automations:
- name: pull-after-push
trigger: post-push
actions:
- type: wait_translate
- type: pull
# Top-level asset policy:
assets:
enabled: true
max_size: 100MB
Ţöþ-ļéṽéļ ƒîéļđš
| Ƒîéļđ | Ţýþé | Đéšçŕîþţîöñ |
|---|---|---|
version | šţŕîñĝ | Šçĥéḿà ṽéŕšîöñ (çüŕŕéñţļý v1) |
name | šţŕîñĝ | Þŕöĵéçţ đîšþļàý ñàḿé |
defaults | öƃĵéçţ | Þŕöĵéçţ-ŵîđé ļàñĝüàĝé, çöñţéẋţ àñđ éẋéçüţîöñ đéƒàüļţš |
collections | ļîšţ | Çöñţéñţ çöļļéçţîöñš (šéé Çöñţéñţ Çöļļéçţîöñš) |
profiles | ḿàþ | Ĝöṽéŕñàñçé ƃöüñđ þéŕ þŕöđüçţ, ķéýéđ ƃý þŕöƒîļé ñàḿé (šéé Þŕöƒîļéš àñđ çĥàññéļš) |
plugins | ḿàþ | Þļüĝîñ đéþéñđéñçîéš àš name: version-constraint |
requires | ḿàþ | Þļüĝîñ ñàḿé → ṽéŕšîöñ çöñšţŕàîñţ ţĥàţ ĝàţéš ļöàđîñĝ; à bowrain: ƃļöçķ àđđš bowrain šö à þļàîñ kapi ƃîñàŕý ŕéƒüšéš ţĥé ŕéçîþé |
flows | ḿàþ | Îñļîñé ƒļöŵ đéƒîñîţîöñš (ƒîļé-þéŕ-ƒļöŵ üñđéŕ .kapi/flows/ àļšö ŵöŕķš) |
ship_gate | ĝàţé | Ţĥé ƃàŕ à ļöçàļé ḿüšţ çļéàŕ ţö ƃé šĥîþþàƃļé (šéé Ĝàţéš) |
ship_gates | ļîšţ | Þéŕ-šçöþé šĥîþ ĝàţéš, éàçĥ à when: šéļéçţöŕ þļüš à ĝàţé |
source_gate | ĝàţé | Ţĥé ƃàŕ ţĥé šöüŕçé ḿüšţ çļéàŕ ƃéƒöŕé à ŕüñ ƒàñš öüţ; none öþţš öüţ |
verified_gate, verified_gates | ĝàţé, ļîšţ | Ţĥé ƃàŕ ƒöŕ à ļöçàļé ţö çöüñţ àš ṽéŕîƒîéđ ƃý à þéŕšöñ |
gates | ḿàþ | Ñàḿéđ ĝàţéš ţĥé ŕüļéš àƃöṽé ḿàý ŕéƒéŕéñçé |
bowrain | öƃĵéçţ | Bowrain-šéŕṽéŕ çöññéçţîöñ çööŕđîñàţéš (ṽéñüé ķéý) |
hooks | ḿàþ | Ƒļöŵš đéçļàŕéđ àţ ļîƒéçýçļé þöîñţš (pre-push, post-pull, …); šçĥéḿà-öñļý, šéé Ĥööķš |
automations | ļîšţ | Ļöçàļ àüţöḿàţîöñ ŕüļéš (šéé Àüţöḿàţîöñš) |
assets | öƃĵéçţ | Àššéţ (îḿàĝé/ƃîñàŕý) þöļîçý |
brand_voice | öƃĵéçţ | À ṽéñüé ķéý ţĥé šéŕṽéŕ àļšö àççéþţš ƒöŕ à þŕöƒîļé àñđ çĥàññéļ ƃîñđîñĝ; ƃîñđ ţĥé ṽöîçé ŵîţĥ defaults.voice öŕ à þŕöƒîļé'š voice: îñšţéàđ |
defaults ƃļöçķ
| Ƒîéļđ | Ţýþé | Đéšçŕîþţîöñ |
|---|---|---|
source_language | šţŕîñĝ | ƂÇÞ-47 šöüŕçé ļàñĝüàĝé (ƒöŕ éẋàḿþļé en) |
target_languages | ļîšţ | ƂÇÞ-47 ţàŕĝéţ ļàñĝüàĝéš; éḿþţý ƒöŕ à šöüŕçé-öñļý þŕöĵéçţ |
collection | šţŕîñĝ | Đéƒàüļţ çöļļéçţîöñ ñàḿé ƒöŕ öŕĝàñîžîñĝ çöñţéñţ |
exclude | ļîšţ | Ĝļöƃ þàţţéŕñš ţö šķîþ đüŕîñĝ šçàññîñĝ |
formats | ḿàþ | Þéŕ-ƒöŕḿàţ đéƒàüļţ þŕéšéţš àñđ çöñƒîĝ öṽéŕŕîđéš |
terms_source | šţŕîñĝ | Þàţĥ ţö ţĥé çöḿḿîţţéđ ţéŕḿš šöüŕçé (ƒöŕ éẋàḿþļé .kapi/terms.json) |
memory_source | šţŕîñĝ | Þàţĥ ţö ţĥé çöḿḿîţţéđ çöñţéñţ ḿéḿöŕý šöüŕçé (ƒöŕ éẋàḿþļé .kapi/memory/memory.json) |
voice | šţŕîñĝ | Þàţĥ ţö ţĥé ṽöîçé þŕöƒîļé éṽéŕý çöļļéçţîöñ îš ĝöṽéŕñéđ ƃý üñļéšš à þŕöƒîļé ƃîñđš àñöţĥéŕ (çöñṽéñţîöñàļļý .kapi/voice.yaml) |
coordinates | ḿàþ | Ţĥé đéçļàŕéđ àẋéš éṽéŕý çöļļéçţîöñ îñĥéŕîţš, brand àñđ mode àḿöñĝ ţĥéḿ; ţĥé šţŕüçţüŕàļ àẋéš product àñđ channel àŕé đéŕîṽéđ ƒŕöḿ channel: àñđ ñéṽéŕ ŵŕîţţéñ ĥéŕé |
materialize | šţŕîñĝ | Ŵĥéñ ţàŕĝéţ ƒîļéš àŕé ŵŕîţţéñ ƒŕöḿ ţĥé þŕöĵéçţ šţöŕé; kapi up --materialize ƒöŕçéš on-converge |
bowrain ƃļöçķ
Öñļý ţĥé çöññéçţîöñ çööŕđîñàţéš šîţ üñđéŕ bowrain::
| Ƒîéļđ | Đéšçŕîþţîöñ |
|---|---|
url | Çöḿþöüñđ ÜŔĻ: <server>/<workspace>/<project-id> öŕ <server>/projects/<id> |
stream | Šéŕṽéŕ-šîđé šţŕéàḿ ţö šýñç àĝàîñšţ; $auto àüţö-đéţéçţš ƒŕöḿ ÇÎ / ĝîţ ƃŕàñçĥ |
converge | Šéŕṽéŕ-šîđé çöñṽéŕĝéñçé þöļîçý: on-push (đéƒàüļţ) öŕ manual |
Ļîƒéçýçļé (hooks, automations) àñđ àššéţ þöļîçý (assets) ļîṽé àţ ţĥé ţöþ ļéṽéļ öƒ ţĥé ŕéçîþé, ñöţ üñđéŕ bowrain:: ţĥéý đéšçŕîƃé þŕöĵéçţ-öŵñéđ þöļîçý, ñöţ šéŕṽéŕ îđéñţîţý.
Ţĥé ƒŕàḿéŵöŕķ ĥàš ñö ƃüîļţ-îñ ñöţîöñ öƒ à šéŕṽéŕ: bowrain: (àñđ hooks:, automations:, assets:, brand_voice:) àŕé bowrain ŕéçîþé éẋţéñšîöñš đéçöđéđ öñļý ŵĥéñ ţĥé kapi-bowrain þļüĝîñ îš îñšţàļļéđ (ţĥé ƒŕàḿéŵöŕķ ŕöüñđ-ţŕîþš ţĥéḿ ṽéŕƃàţîḿ öţĥéŕŵîšé). Ţĥé ķéý îš ţĥé þļàţƒöŕḿ'š öŵñ ñàḿé ƃéçàüšé ţĥé ƃļöçķ îš ţĥé þļàţƒöŕḿ: kapi ƒîñđš îţ ţĥŕöüĝĥ à ṽéñüé ƒļàĝ öñ ţĥé þļüĝîñ'š šçĥéḿà ŕéĝîšţŕàţîöñ àñđ ŕéàđš öñļý url: àñđ converge:, ñéṽéŕ šþéļļîñĝ ţĥé ķéý öüţ. Šö kapi init / kapi init-connect (àñđ kapi config server.url …) đéçļàŕé requires: { bowrain: "*" } ŵĥéñéṽéŕ ţĥéý ŵŕîţé à bowrain: ƃļöçķ. À þļàîñ kapi ƃîñàŕý ŵîţĥöüţ ţĥé þļüĝîñ ţĥéñ ŕéƒüšéš ţĥé ŕéçîþé ŵîţĥ àñ àçţîöñàƃļé "ŕéǫüîŕéš ţĥé bowrain þļüĝîñ" éŕŕöŕ ŕàţĥéŕ ţĥàñ šîļéñţļý îĝñöŕîñĝ ţĥé çöññéçţîöñ. Šéé Ç-01: Ţĥé þŕöĵéçţ ḿöđéļ.
Çöñţéñţ Çöļļéçţîöñš
Éàçĥ éñţŕý üñđéŕ collections: îš à çöñţéñţ çöļļéçţîöñ. Ƃàŕé éñţŕîéš àŕé šîñĝļé-þàţţéŕñ çöļļéçţîöñš; ñàḿéđ çöļļéçţîöñš ĝŕöüþ ḿüļţîþļé îţéḿš ţöĝéţĥéŕ.
Ýöü çàñ éđîţ collections: ƃý ĥàñđ, öŕ ŵîţĥ ţĥé çöŕé kapi çöḿḿàñđš (ñö bowrain þļüĝîñ ŕéǫüîŕéđ; ţĥéý öñļý ţöüçĥ ţĥé ļöçàļ ŕéçîþé):
kapi add "src/**/*.json" # append a content pattern (format auto-detected)
kapi rm "src/legacy/*.json" # remove the mapping, or add to the exclude list
kapi ls # list the files the content tracks
kapi add "src/**/*.md" --format markdown # pass --format only to override detection
kapi ls --stats # with per-file block and word counts
add/rm/ls àŕé ƒŕàḿéŵöŕķ çöḿḿàñđš; šýñç šţàţé (çĥàñĝéđ-ṽš-šéŕṽéŕ) îš kapi status.
collections:
# Bare entry: single source pattern
- path: src/locales/**/*.json
format: json
# With output path template
- path: content/docs/**/*.md
format: markdown
target: i18n/{lang}/docs/{path}/{filename}
# Per-entry overrides
- path: legacy/**/*.properties
format: java-properties
source_language: en-GB
collection: legacy
# Named collection: its items live under content:, relative to base:
- name: ui
channel: app
base: src
preview:
kind: storybook
url: https://storybook.example.com
content:
- path: "**/*.tsx"
format:
name: exec
config:
command: "vp neokapi-i18n extract --stream"
- path: "i18n/en/*.json"
format: json
Çöļļéçţîöñ ƒîéļđš
| Ƒîéļđ | Ţýþé | Đéšçŕîþţîöñ |
|---|---|---|
name | šţŕîñĝ | Çöļļéçţîöñ ñàḿé; ŕéǫüîŕéđ ţö ƃîñđ à channel: |
base | šţŕîñĝ | Ţĥé đîŕéçţöŕý ţĥîš çöļļéçţîöñ ļîṽéš îñ; éṽéŕý path, target àñđ îţéḿ base ƃéļöŵ îš ŵŕîţţéñ ŕéļàţîṽé ţö îţ |
channel | šţŕîñĝ | Ţĥé þöîñţ îñ ţĥé çöñţéẋţ šþàçé ţĥîš çöñţéñţ šîţš àţ: profile/channel, öŕ à ƃàŕé channel (šéé Þŕöƒîļéš àñđ çĥàññéļš) |
coordinates | ḿàþ | Đéçļàŕéđ àẋéš ţĥîš çöļļéçţîöñ šéţš, öṽéŕļàîđ öñ defaults.coordinates þéŕ àẋîš: à çöļļéçţîöñ ḿöṽéš öñ ţĥé öñé àẋîš îţ đéŕš öñ àñđ îñĥéŕîţš ţĥé ŕéšţ |
source_only | ƃööļ | Ţĥîš çöļļéçţîöñ ĥàš ñö ţàŕĝéţ ļàñĝüàĝé àñđ îš ñéṽéŕ ţŕàñšļàţéđ: à ŕüñ ŕéàđš îţ, çĥéçķš îţ, àñđ ŵŕîţéš ñöţĥîñĝ ƃàçķ |
preview | öƃĵéçţ | Ŵĥéŕé ţĥîš çöļļéçţîöñ'š šţŕîñĝš çàñ ƃé ŕéàđ îñ þļàçé: kind (storybook) àñđ url (šéé Ŕéṽîéŵ îñ þļàçé) |
content | ļîšţ | Ţĥé çöļļéçţîöñ'š çöñţéñţ îţéḿš |
collection | šţŕîñĝ | Çöļļéçţîöñ ŕöüţîñĝ öṽéŕŕîđé |
source_language | šţŕîñĝ | Šöüŕçé ļàñĝüàĝé öṽéŕŕîđé |
target_languages | ļîšţ | Ţàŕĝéţ ļàñĝüàĝé öṽéŕŕîđé |
Çöñţéñţ îţéḿ ƒîéļđš
| Ƒîéļđ | Ţýþé | Đéšçŕîþţîöñ |
|---|---|---|
path | šţŕîñĝ | Ĝļöƃ þàţţéŕñ ƒöŕ šöüŕçé ƒîļéš (šüþþöŕţš {lang} þļàçéĥöļđéŕ) |
format | šţŕîñĝ / öƃĵéçţ | Ƒîļé ƒöŕḿàţ ÎĐ (ƒöŕ éẋàḿþļé json, html) öŕ öƃĵéçţ ŵîţĥ name/config/preset |
target | šţŕîñĝ | Öüţþüţ þàţĥ þàţţéŕñ ƒöŕ ţàŕĝéţ ƒîļéš (šüþþöŕţš {lang} àñđ {path}) |
base | šţŕîñĝ | Đîŕéçţöŕý à ḿàţçĥéđ ƒîļé'š þàţĥ îš ḿàđé ŕéļàţîṽé ţö ƒöŕ ţàŕĝéţ-ţöķéñ éẋþàñšîöñ; đéƒàüļţš ţö ţĥé ĝļöƃ'š ƒîẋéđ þŕéƒîẋ |
collection | šţŕîñĝ | Çöļļéçţîöñ ŕöüţîñĝ öṽéŕŕîđé ƒöŕ ţĥîš éñţŕý |
source_language | šţŕîñĝ | Šöüŕçé ļàñĝüàĝé öṽéŕŕîđé ƒöŕ ţĥîš éñţŕý |
target_languages | ļîšţ | Ţàŕĝéţ ļàñĝüàĝé öṽéŕŕîđé ƒöŕ ţĥîš éñţŕý |
assets | öƃĵéçţ | Þéŕ-éñţŕý àššéţ þöļîçý öṽéŕŕîđé |
asset_max_size | šţŕîñĝ | Þéŕ-éñţŕý àššéţ ḿàẋ šîžé öṽéŕŕîđé |
À ƃàŕé éñţŕý çàŕŕîéš path, format àñđ target đîŕéçţļý öñ ţĥé çöļļéçţîöñ àñđ ĥàš ñö content: ļîšţ.
Þŕöƒîļéš àñđ çĥàññéļš
Çöñţéñţ îš ŵŕîţţéñ ƒöŕ à þöîñţ îñ ţĥé çöñţéẋţ šþàçé. Ţŵö öƒ îţš àẋéš àŕé šţŕüçţüŕàļ: ţĥé þŕöđüçţ îţ ƃéļöñĝš ţö àñđ ţĥé çĥàññéļ îţ šĥîþš öñ. À ķéý üñđéŕ profiles: îš à þŕöđüçţ, ţĥé çĥàññéļš ţĥàţ þŕöƒîļé đéçļàŕéš àŕé ţĥé çĥàññéļš ţĥàţ þŕöđüçţ šĥîþš öñ, àñđ à ñàḿéđ çöļļéçţîöñ ñàḿéš îţš þöîñţ ŵîţĥ öñé channel: ŕéƒéŕéñçé. Ţĥé đéçļàŕéđ àẋéš (brand, mode, àñđ àñý ţĥé þŕöĵéçţ àđđš) çöḿé ƒŕöḿ defaults.coordinates àñđ à çöļļéçţîöñ'š öŵñ coordinates:.
profiles:
acme:
channels: [app, docs]
voice: .kapi/voice.yaml
acme-labs:
channels: [app]
voice: .kapi/profiles/acme-labs/voice.yaml
termstore: .kapi/profiles/acme-labs/terms.json
valid_from: 2026-09-01
collections:
- name: acme-docs
channel: docs # only acme declares it; the bare form resolves
content:
- path: docs/**/*.md
- name: labs-app
channel: acme-labs/app # both declare `app`; qualify it
content:
- path: labs/src/i18n/**/*.kbf.json
| Ƒîéļđ | Đéšçŕîþţîöñ |
|---|---|
channels | Ţĥé çĥàññéļš ţĥîš þŕöđüçţ šĥîþš öñ |
voice | Ţĥé ṽöîçé þŕöƒîļé ţĥàţ ĝöṽéŕñš ţĥîš þŕöđüçţ, öṽéŕŕîđîñĝ defaults.voice |
termstore | À ţéŕḿš šţöŕé ƃöüñđ ƒöŕ ţĥîš þŕöđüçţ'š ļöçàļ ŕüñš |
concept | À çöñçéþţ ŕéƒéŕéñçé (term:<id>) ţĥé þŕöƒîļé çàŕŕîéš ƒöŕ đîšþļàý |
valid_from, valid_to | Ţĥé þŕöƒîļé'š ṽàļîđîţý ŵîñđöŵ |
Ţĥé þŕöƒîļé ñàḿé îš àļšö ţĥé đîŕéçţöŕý üñđéŕ .kapi/profiles/<name>/ ĥöļđîñĝ ŵĥàţ ţĥàţ þŕöƒîļé öṽéŕŕîđéš. Þŕöƒîļé ñàḿéš àñđ çĥàññéļš àŕé šļüĝš (ļöŵéŕçàšé ļéţţéŕš, đîĝîţš àñđ ĥýþĥéñš): šţàƃļé îđéñţîƒîéŕš ţĥàţ çŕöšš ţĥé šýñç ŵîŕé àš ţĥé çöñţéñţ'š þŕöđüçţ àñđ çĥàññéļ çööŕđîñàţéš, ñéṽéŕ ṽöçàƃüļàŕý. À ƃàŕé channel: ţŵö þŕöƒîļéš đéçļàŕé îš à ļöàđ éŕŕöŕ ñàḿîñĝ ƃöţĥ ǫüàļîƒîéđ šþéļļîñĝš; à çöļļéçţîöñ ƃîñđîñĝ ñö çĥàññéļ îš ĝöṽéŕñéđ ƃý defaults.voice àñđ ţĥé þŕöĵéçţ'š öŵñ ţéŕḿš.
À þŕöƒîļé'š termstore: îš ţĥé öñé ƃîñđîñĝ ţĥàţ đöéš ñöţ çŕöšš ţö ţĥé šéŕṽéŕ, ŵĥîçĥ ĝöṽéŕñš ţéŕḿš ƒŕöḿ ţĥé ŵöŕķšþàçé ṽöçàƃüļàŕý îñšţéàđ. À çöññéçţéđ þŕöĵéçţ ţĥàţ ƃîñđš à ţéŕḿš šţöŕé þéŕ þŕöƒîļé ŵàŕñš öñ éṽéŕý ŕüñ ţĥàţ ţĥé ƃîñđîñĝ àþþļîéš ţö ļöçàļ ŕüñš öñļý.
Ĝàţéš
À ĝàţé ñàḿéš ţĥé ŕüñĝ öƒ ţĥé ţàŕĝéţ ļàđđéŕ à šçöþé ḿüšţ ŕéàçĥ, àñđ ŵĥö ḿàý ĥàṽé àþþŕöṽéđ îţ, ƒöŕ ţĥé šçöþé ţö çöüñţ àš šĥîþþàƃļé. ship_gate šéţš ţĥé ƃàŕ ƒöŕ éṽéŕý ļöçàļé; ship_gates ŕéƒîñéš îţ þéŕ šçöþé ŵîţĥ à when: šéļéçţöŕ, šö à ļéĝàļ çöļļéçţîöñ çàñ ŵàîţ ƒöŕ à þéŕšöñ ŵĥîļé à ĥéļþ çöļļéçţîöñ šĥîþš öñ çĥéçķš; source_gate îš ţĥé šöüŕçé-šîđé ƃàŕ à ŕüñ ḿüšţ çļéàŕ ƃéƒöŕé îţ ƒàñš öüţ, àñđ source_gate: none öþţš öüţ. Ţĥé šĥîþ šţàţé ţĥé šéŕṽéŕ đéŕîṽéš ƒŕöḿ ţĥéšé îš đéšçŕîƃéđ üñđéŕ Šĥîþ šţàţéš.
Ţööļ çöñƒîĝüŕàţîöñ
À ƒļöŵ šţéþ'š config: ķéýš àŕé ţĥé ţööļ'š öŵñ šçĥéḿà ķéýš, ļîšţéđ îñ ţĥé
ţööļ ŕéƒéŕéñçé. Ţĥé
ŵöŕđîñĝ çöñšţŕàîñţ îš öñé ķéý éṽéŕýŵĥéŕé îţ àþþļîéš: term-check, translate
àñđ recycle ţàķé term_rules:, à ļîšţ öƒ ŕüļéš ñàḿîñĝ à ţéŕḿ, ŵĥàţ ţö üšé
îñšţéàđ, àñđ ĥöŵ ĥàŕđ ţĥé ŕüļé ƃîţéš. Ţĥé ṽöîçé þŕöƒîļé çàŕŕîéš ţĥé šàḿé ŕüļéš
üñđéŕ îţš vocabulary: šéçţîöñ, àñđ à ŕüñ ŕéšöļṽéš ţĥé ţéŕḿš îñ ƒöŕçé àţ à
ƃļöçķ'š þöîñţ ƒŕöḿ ţĥé þŕöƒîļé àñđ ţĥé ƃöüñđ ţéŕḿš šţöŕé.
Ƒöŕḿàţ öƃĵéçţ ƒöŕḿ
Ŵĥéñ ýöü ñééđ ţö çöñƒîĝüŕé à ƒöŕḿàţ (àþþļý à þŕéšéţ, þàšš öþţîöñš, ŕüñ à šüƃþŕöçéšš éẋţŕàçţöŕ) üšé ţĥé öƃĵéçţ ƒöŕḿ:
collections:
- path: "src/**/*.tsx"
format:
name: exec
config:
command: "vp neokapi-i18n extract --stream"
- path: "docs/**/*.html"
format:
name: html
preset: strict-extraction
Àüţöḿàţîöñš
Àüţöḿàţîöñš àŕé ŕüļéš ţĥàţ ŕüñ àüţöḿàţîçàļļý àţ ļîƒéçýçļé þöîñţš, đéçļàŕéđ àţ ţĥé ţöþ ļéṽéļ öƒ ţĥé ŕéçîþé:
automations:
- name: checks-before-push
trigger: pre-push
actions:
- type: run_flow
config:
flow: qa
- type: wait_translate
- name: auto-pull-after-push
trigger: post-push
actions:
- type: pull
Àüţöḿàţîöñ ƒîéļđš
| Ƒîéļđ | Đéšçŕîþţîöñ |
|---|---|
name | Ŕüļé ñàḿé |
trigger | Ļîƒéçýçļé þöîñţ: pre-push, post-push, pre-pull, post-pull, pre-flow, post-flow |
actions | Ļîšţ öƒ àçţîöñš (run_flow, wait_translate, pull, push) |
enabled | Öþţîöñàļ ƃööļéàñ (đéƒàüļţš ţö true) |
Ţĥé ţöþ-ļéṽéļ hooks: ḿàþ îš ṽàļîđàţéđ ƃüţ ñöţ éẋéçüţéđ; šéé Ĥööķš.
Þŕöĵéçţ Đîšçöṽéŕý
kapi šéàŕçĥéš ƒöŕ à kapi.yaml ŕéçîþé ƃý ŵàļķîñĝ üþ ţĥé đîŕéçţöŕý ţŕéé (ļîķé ĝîţ):
cd my-app/src/locales/fr/
kapi status # finds kapi.yaml at ../../../kapi.yaml
Àļļ çöḿḿàñđš ŵöŕķ ƒŕöḿ àñý šüƃđîŕéçţöŕý ŵîţĥîñ ţĥé þŕöĵéçţ. À đîŕéçţöŕý ĥöļđš àţ ḿöšţ öñé kapi.yaml, šö đîšçöṽéŕý îš üñàḿƃîĝüöüš; àñ éẋþļîçîţ -p <path> šţîļļ öṽéŕŕîđéš îţ.
Ṽéŕšîöñ Çöñţŕöļ
Çöḿḿîţ ţö ĝîţ
kapi.yaml: ţĥé ŕéçîþé (šîñĝļé šöüŕçé öƒ ţŕüţĥ ƒöŕ çöñƒîĝüŕàţîöñ).kapi/terms.json,.kapi/memory/memory.json,.kapi/voice.yaml: ţĥé çöñţéẋţ šöüŕçéš ţĥé ŕéçîþé ƃîñđš.kapi/state/*.jsonl: ţĥé üñîţ-šţàţé ŕéçöŕđ.kapi/flows/*.yaml: ƒîļé-þéŕ-ƒļöŵ đéƒîñîţîöñš, îƒ ýöü üšé ţĥéḿ.kapi/manifest.yaml,.kapi/filters.json: ƃööķķééþîñĝ àñđ šĥàŕéđ ŕéàđéŕ çöñƒîĝüŕàţîöñ
Đö ÑÖŢ çöḿḿîţ
kapi init ŵŕîţéš à ţŵö-ļîñé îĝñöŕé ŕüļé, ŵîţĥ ñö ñéĝàţîöñ:
/.kapi/work/
/.kapi/filters.local.json
.kapi/work/: éṽéŕýţĥîñĝ đéŕîṽéđ:store.db, ţĥé çàçĥéš, àñđ ţĥé ŕéđàçţîöñ ṽàüļţ.kapi/filters.local.json: ýöüŕ þéŕšöñàļ ŕéàđéŕ öṽéŕŕîđéš
Đéļéţîñĝ .kapi/work/cache/ çöšţš ñöţĥîñĝ. Đéļéţîñĝ .kapi/work/ çöšţš ţŵö ţĥîñĝš: ţĥé ŕéṽîéŵ üñîţ šţàţé šţàĝéđ šîñçé ţĥé ļàšţ kapi commit, ŵĥîçĥ ļîṽéš öñļý îñ store.db (ŕüñ kapi commit ƃéƒöŕé ýöü ŕéḿöṽé îţ), àñđ, îƒ ţĥé þŕöĵéçţ üšéš ŕéđàçţîöñ, ţĥé ŵîţĥĥéļđ öŕîĝîñàļš îñ .kapi/work/vault/, ŵĥîçĥ àŕé ļöçàļ-öñļý ƃý đéšîĝñ àñđ ŕéƃüîļđ ƒŕöḿ ñöţĥîñĝ.
Îñîţîàļîžàţîöñ
Çŕéàţé à ñéŵ bowrain þŕöĵéçţ:
cd my-app/
kapi init
Îñ îñţéŕàçţîṽé ḿöđé (đéƒàüļţ ŵĥéñ šţđîñ îš à ţéŕḿîñàļ), kapi init þŕéšéñţš à ĝüîđéđ šéţüþ ŵîžàŕđ ŵĥéŕé ýöü çàñ šîĝñ îñ, çĥööšé à ŵöŕķšþàçé, àñđ çöñƒîĝüŕé ýöüŕ þŕöĵéçţ.
Ƒöŕ ñöñ-îñţéŕàçţîṽé üšàĝé (ƒöŕ éẋàḿþļé ÇÎ/ÇĐ), üšé ƒļàĝš:
# Local-only project (no bowrain: block written)
kapi init --source en --targets fr,de,ja
# Connect to a server (anonymous claim)
kapi init --server https://app.bowrain.cloud --anonymous
# Apply a framework preset
kapi init --preset nextjs
# Connect to an existing project
kapi init --server https://app.bowrain.cloud --project abc123
Îñîţ ƒļàĝš
| Ƒļàĝ | Đéšçŕîþţîöñ |
|---|---|
--server | Šéŕṽéŕ ÜŔĻ |
--workspace | Çŕéàţé ţĥé þŕöĵéçţ îñ ţĥîš ŵöŕķšþàçé (šļüĝ) |
--project | Çöññéçţ ţö àñ éẋîšţîñĝ þŕöĵéçţ ƃý ÎĐ |
--name | Þŕöĵéçţ ñàḿé (đéƒàüļţ: çüŕŕéñţ đîŕéçţöŕý ñàḿé) |
--source | Šöüŕçé ļöçàļé (đéƒàüļţ: en) |
--targets | Ţàŕĝéţ ļöçàļéš, çöḿḿà-šéþàŕàţéđ (ƒöŕ éẋàḿþļé nb,fr) |
--anonymous | Çŕéàţé à þŕöĵéçţ ŵîţĥöüţ šîĝñîñĝ îñ |
--email | Çŕéàţé à þŕöĵéçţ àñđ éḿàîļ à ļîñķ ţö çļàîḿ îţ |
--preset | Àþþļý à ƒŕàḿéŵöŕķ þŕéšéţ (ƒöŕ éẋàḿþļé nextjs, react-intl, angular) |
kapi init ŵŕîţéš:
kapi.yamlŕéçîþé àţ ţĥé þŕöĵéçţ ŕööţ (ŵîţĥ àbowrain:ƃļöçķ ŵĥéñ à šéŕṽéŕ ŵàš šüþþļîéđ).kapi/đîŕéçţöŕý.kapi/flows/pseudo.yaml, àñ éẋàḿþļé ƒļöŵ- à ţŵö-ļîñé îĝñöŕé ŕüļé:
/.kapi/work/àñđ/.kapi/filters.local.json
Šéŕṽéŕ Çöññéçţîöñ
Ţĥé ƃļöçķ'š url ƒîéļđ îš à çöḿþöüñđ ÜŔĻ ţĥàţ éñçöđéš ţĥé šéŕṽéŕ àđđŕéšš, ŵöŕķšþàçé, àñđ þŕöĵéçţ ÎĐ:
bowrain:
# Workspace project
url: https://app.bowrain.cloud/my-team/abc123
# Direct project (no workspace)
# url: https://app.bowrain.cloud/projects/abc123
stream: $auto
Öñçé çöññéçţéđ, ýöü çàñ šýñç ŵîţĥ ţĥé šéŕṽéŕ:
kapi push # Upload local changes to the server
kapi pull # Fetch results and recipe changes from the server
kapi status # Coverage, ship standing, and the server delta
Ţĥé àçţîṽé šéŕṽéŕ ÜŔĻ îš ŕéšöļṽéđ ƒŕöḿ (ƒîŕšţ ḿàţçĥ ŵîñš):
url:üñđéŕ ţĥé ŕéçîþé'šbowrain:ƃļöçķ--serverƒļàĝBOWRAIN_SERVER_URLéñṽîŕöñḿéñţ ṽàŕîàƃļé /server.urlîñ ţĥé þéŕ-ḿàçĥîñé bowrain çöñƒîĝ- Éẋîšţîñĝ àüţĥ šţàţé (ƒŕöḿ
kapi auth login) - Ţĥé ĥöšţéđ šéŕṽîçé (
https://app.bowrain.cloud): çöḿḿàñđš ţĥàţ çöñţàçţ à šéŕṽéŕ ƒàļļ ƃàçķ ţö îţ; šéļƒ-ĥöšţéđ đéþļöýḿéñţš çöñƒîĝüŕé öñé öƒ ţĥé àƃöṽé