This guide covers how to add, update, and translate content for the sap-devs CLI — packs, adapters, and profiles.
Content is loaded from up to four sources, merged by id with later layers overriding earlier ones:
| Layer | Location | Purpose |
|---|---|---|
| Official | ~/.cache/sap-devs/official/content/ (Linux), ~/Library/Caches/sap-devs/official/content/ (macOS), or %LOCALAPPDATA%/sap-devs/cache/official/content/ (Windows) |
Fetched from the official repo via sap-devs sync |
| Company | ~/.cache/sap-devs/company/content/ (Linux), ~/Library/Caches/sap-devs/company/content/ (macOS), or %LOCALAPPDATA%/sap-devs/cache/company/content/ (Windows) |
Optional; configured via sap-devs config company <url> |
| User | ~/.local/share/sap-devs/ (Linux), ~/Library/Application Support/sap-devs/data/ (macOS), %LOCALAPPDATA%/sap-devs/data/ (Windows) |
Per-user overrides |
| Project | .sap-devs/ in the current working directory |
Per-project overrides |
If two layers define a pack with the same id, the later layer wins completely — unless the later pack has additive: true, in which case it augments the earlier pack rather than replacing it. See Additive Layers.
Each pack lives in content/packs/<pack-id>/. All files are optional except pack.yaml.
content/packs/cap/
├── pack.yaml # Pack metadata
├── context.md # AI context text (English)
├── context.de.md # German AI context text (optional)
├── tips.md # Tips (English)
├── tips.de.md # German tips (optional)
├── tools.yaml # Tool version requirements
├── resources.yaml # Curated resource links
├── mcp.yaml # MCP server definitions
├── samples.yaml # Canonical code samples
├── tutorials.yaml # Curated tutorial references
├── learning.yaml # Curated learning journey references
├── discovery.yaml # Discovery Center mission/service/guidance references
└── paths.yaml # Curated learning paths
id: cap # unique slug — used as the merge key
name: SAP Cloud Application Programming Model
description: Node.js and Java framework for building cloud-native business applications on BTP
tags: [cloud, btp, nodejs, java, odata, cds]
profiles: [cap-developer, btp-developer] # informational metadata only — see note below
weight: 100 # default weight; profiles can override this per-pack
overlaps: [] # optional — see note below
locales:
de:
name: SAP Cloud Application Programming Model
description: Node.js- und Java-Framework für Cloud-native Business-Anwendungen auf BTP
versions:
"@sap/cds": "9.8.0" # optional — latest known versions for staleness checks
"@sap/cds-dk": "9.8.0"Note:
weightsets the default priority for this pack. Individual profiles can override it in theirpackslist (see Profiles below).Note: The
profilesfield is informational metadata only. A pack is only active when it is explicitly listed in a profile'spackslist. Adding a pack id toprofileshere does not automatically include it in that profile.Note:
overlapsis a list of pack IDs whose content subsumes this pack's content. If any listed pack is present at a higher weight during injection, this pack is automatically dropped to avoid redundant context. Semantics: "ifcapis already included, my content adds nothing new — drop me." Only the lower-weight pack carries the declaration. Omit the field (or leave it empty) if no overlap exists.Note:
base(optional bool, defaultfalse) — whentrue, this pack is a base pack: it is auto-injected into every profile regardless of theprofilesfield, always rendered first (before profile-specific packs), and exempt from adapter byte-budget trimming and overlap deduplication. Theprofilesfield is irrelevant for base packs and should be omitted. Authoring contract: keep base pack content minimal — base packs consume tokens in every context window. Note: declaringoverlaps: [base]on a non-base pack has no effect (the base pack is separated before the deduplication pass runs). This is a known limitation.Note:
additive(optional bool, defaultfalse) — whentrue, this pack augments the lower-layer pack with the sameidrather than replacing it. Only valid in company, user, or project layers. See Additive Layers.Note:
additive_position(optional string"before"|"after", default"after") — controls where additive content is inserted relative to the base pack's content. Only meaningful whenadditive: true.Note:
versions(optional map of string → string) — latest known versions for dependency staleness checks, e.g."@sap/cds": "9.8.0". Used bysap-devs doctorandsap-devs injectto flag outdated project dependencies. When multiple packs declare the same key, the highest-weight pack wins.
Free-form Markdown injected as AI context into tools. No special syntax. May be long-form reference material, code examples, or conventions.
Localised version of context.md for language <lang> (e.g. context.de.md). Falls back to context.md if absent for the active language.
H2-delimited tips. Each tip:
## Use cds watch for local development
Tags: cap,nodejs
Run `cds watch` instead of `node server.js` — it reloads on every file change.
## Define managed entities for audit fields
Tags: cap,cds
Add `: managed` to your entities to get createdAt, createdBy, modifiedAt, modifiedBy for free.## <Title>— requiredTags: tag1,tag2— optional, one line immediately after the heading- Body — tip content, may be multi-line Markdown
Localised version of tips.md. Same format.
- id: nodejs
name: Node.js
required: ">=18.0.0" # semver constraint
detect:
command: "node --version"
pattern: "v(\\d+\\.\\d+\\.\\d+)" # regex capturing the version string
install:
windows: "winget install OpenJS.NodeJS.LTS"
macos: "brew install node@20"
linux: "nvm install 20"
docs: "https://nodejs.org"
- id: cds-dk
name: SAP CDS CLI
required: ">=7.0.0"
detect:
command: "cds --version"
pattern: "@sap/cds: (\\d+\\.\\d+\\.\\d+)"
install:
all: "npm install -g @sap/cds-dk" # "all" key applies to every platform
docs: "https://cap.cloud.sap"- id: cap/docs-official # <pack-id>/<slug>
title: CAP Documentation
url: https://cap.cloud.sap/docs
type: official-docs # official-docs | sample | community | tutorial | blog
tags: [reference, getting-started]MCP server definitions for the pack. The file is a YAML list of MCPServer entries.
- id: cap-mcp # unique slug within the pack
name: CAP MCP Server
description: MCP server for SAP CAP development
install:
command: "npx" # executable to run
args: ["-y", "@sap/cap-mcp"] # arguments passed to the command
hosts: [claude-code, cursor] # AI tools that should register this serverFields:
id— unique identifier (combined withPackIDat load time)name— human-readable display namedescription— short description shown insap-devs mcp listinstall.command— executable to run (e.g.npx,node,python)install.args— list of arguments passed to the commandhosts— list of AI tool IDs that should register this server (used bysap-devs mcp install)
Note: All existing
mcp.yamlfiles in official packs are currently stubs pending Plan 2 implementation.
Curated learning paths that combine tutorials, learning journeys, and Discovery Center missions into ordered sequences. Used by sap-devs learn path list/show/open.
paths:
- id: cap-getting-started # unique path identifier (kebab-case)
name: Getting Started with CAP
level: beginner # optional: beginner, intermediate, advanced
description: Build your first CAP application from project creation to deployment
steps:
- type: journey # journey, tutorial, or mission
slug: developing-with-sap-cloud-application-programming-model
- type: tutorial
slug: cap-getting-started
- type: mission
slug: "4327" # mission IDs are stringified integersFields per path:
id— unique identifier across all packsname— human-readable path namedescription— short description shown inpath showlevel— optional experience level filter (beginner,intermediate,advanced)steps— ordered list of content references; each has atypeandslug
Packs without a paths.yaml get auto-generated paths from their featured content (grouped by level, minimum 2 items per level).
Adapters define how to push content into a specific AI tool. Files live in content/adapters/<tool-id>.yaml.
id: claude-code
name: Claude Code
type: file-inject # file-inject | clipboard-export | mcp-wire
max_tokens: 0 # optional — token budget for this adapter; 0 = unconstrained
targets:
- scope: global # global (user-level) or project (current dir)
path: "~/.claude/CLAUDE.md"
mode: replace-section # replace-section | append
section: "SAP Developer Context"
- scope: project
path: "./CLAUDE.md"
mode: replace-section
section: "SAP Developer Context"
detect:
- command: "claude --version" # run this; if it exits 0, tool is present
- path: "~/.claude" # or check if this path exists
mcp_config:
path: "~/.claude/settings.json"
format: json
key: "mcpServers"Modes:
replace-section— replaces the named fenced section. The injected block is wrapped with<!-- sap-devs:start:SAP Developer Context -->and<!-- sap-devs:end:SAP Developer Context -->markers.append— defined in the adapter schema but not yet implemented; using it causes a runtime error. Do not use until engine support is added.
Types:
file-inject— writes context to a config file. Used bysap-devs inject.clipboard-export— copies context to clipboard (global scope only).mcp-wire— registers MCP servers in the tool's JSON config. Used bysap-devs mcp install, notinject.
max_tokens: When set to a positive integer, the inject engine trims packs to fit within approximately max_tokens × 4 bytes of rendered context before writing to this adapter. Higher-weight packs are always included first; the engine stops at the first pack that would exceed the budget. Zero (the default) means unconstrained — all packs are included. Use sap-devs inject --stats --dry-run to see how many tokens each adapter is currently using.
Profiles tag which packs belong to a developer persona. Files live in content/profiles/<profile-id>.yaml.
id: cap-developer
name: CAP Developer
description: Building cloud-native apps with SAP CAP on BTP
packs:
- id: cap
weight: 100 # higher weight = appears earlier in rendered context
- id: btp-core
weight: 80
- id: abap
weight: 10
tip_tags: [cap, nodejs, odata, cds, btp] # tips with these tags are preferred for this profileApplyWeights() reorders packs so higher-weight packs appear first in the rendered context injected into AI tools.
Two profiles are built into the CLI and require no YAML file on disk:
| Profile | Behaviour |
|---|---|
all |
Includes every pack from every content layer, sorted by pack weight. Use for development or when working across multiple SAP domains. |
minimal |
Includes base packs only — no technology-specific content. Use for cost-conscious setups or AI tools with tight token budgets. |
Both profiles appear in sap-devs profile list and can be set with sap-devs profile set all or sap-devs profile set minimal.
Reserved IDs: The IDs all and minimal are reserved. Any file named all.yaml or minimal.yaml in a content layer is silently ignored — the built-in definition always takes precedence.
-
Create the directory:
mkdir content/packs/<new-id>
-
Write
pack.yamlwith a uniqueid:id: my-pack name: My Pack description: What this pack covers tags: [tag1, tag2] profiles: [cap-developer] # or whichever profile(s) should include it
-
Add content files as needed (
context.md,tips.md,tools.yaml,resources.yaml). All are optional. -
Reference the pack in at least one profile:
# content/profiles/cap-developer.yaml packs: - id: my-pack weight: 50
-
Test with dev mode:
SAP_DEVS_DEV=1 go run . inject --dry-runVerify the new context appears in the output.
Base packs: If your pack should be auto-injected into every profile (e.g. shared ecosystem links), add
base: trueand omit theprofilesfield. Keep base pack content short — it is included in every context window regardless of budget constraints.
Edit the file in the relevant layer. The official layer is under content/packs/ in this repo. To override official content without modifying it:
- User override: Copy the pack to
~/.local/share/sap-devs/packs/<id>/(Linux) and edit there. - Project override: Copy to
.sap-devs/packs/<id>/in the project directory.
The override pack must use the same id as the official pack. The later layer wins.
The active language is resolved in this order:
sap-devs config set language <tag>— explicit config settingLANGenvironment variable (e.g.de_AT.UTF-8→de)LC_ALLenvironment variable- Fallback:
en
Region and encoding suffixes are stripped: de_AT.UTF-8 → de. If the resolved tag has no catalog, it falls back to en.
Add localised content files alongside the base files:
content/packs/cap/
├── context.md # base (English)
├── context.de.md # German translation
├── tips.md # base (English)
└── tips.de.md # German translation
The loader automatically picks the locale-specific file when the active language matches. Falls back to the base file if the locale file is absent.
Add a locales block to pack.yaml:
locales:
de:
name: SAP Cloud Application Programming Model
description: Node.js- und Java-Framework für Cloud-native Business-Anwendungen auf BTP
fr:
name: SAP Cloud Application Programming Model
description: Framework Node.js et Java pour applications cloud natives sur BTPCLI strings live in internal/i18n/catalogs/<lang>.json. Copy en.json as a starting point — any missing keys fall back to English automatically.
cp internal/i18n/catalogs/en.json internal/i18n/catalogs/fr.json
# Edit fr.json with French translationsKey naming convention: <command>.<subcommand>.<string_name>
{
"inject.short": "Pousser le contexte SAP dans vos outils IA",
"inject.done": "Contexte SAP injecté (portée {{.Scope}})."
}Values may be plain strings or Go text/template expressions (e.g. {{.Scope}}).
In Go code, strings are looked up with:
i18n.T(lang, key)— plain stringi18n.Tf(lang, key, data)— template string, wheredataismap[string]any- Pass
i18n.ActiveLangas thelangargument
-
Create the CLI catalog:
cp internal/i18n/catalogs/en.json internal/i18n/catalogs/<lang>.json
Translate values. Leave any untranslated keys as-is (they fall back to English).
-
Add localised content files to each pack you want to translate:
# For each pack: cp content/packs/<id>/context.md content/packs/<id>/context.<lang>.md cp content/packs/<id>/tips.md content/packs/<id>/tips.<lang>.md # Edit the new files with translations
-
Add
localesblocks to eachpack.yamlyou translated. -
Test:
sap-devs config set language <lang> SAP_DEVS_DEV=1 go run . inject --dry-run SAP_DEVS_DEV=1 go run . tip
Verify translated strings appear in output.
-
Reset language when done testing:
sap-devs config set language en