sap-devs Content Guide
This guide covers how to add, update, and translate content for the sap-devs CLI — packs, adapters, and profiles.
Overview: The Content Layer System
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.
Pack Structure
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 pathspack.yaml
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.
context.md
Free-form Markdown injected as AI context into tools. No special syntax. May be long-form reference material, code examples, or conventions.
context.<lang>.md
Localised version of context.md for language <lang> (e.g. context.de.md). Falls back to context.md if absent for the active language.
tips.md
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
tips.<lang>.md
Localised version of tips.md. Same format.
tools.yaml
- 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"resources.yaml
- 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.yaml
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.
paths.yaml
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
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
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.
Built-in Profiles
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.
Creating a New Pack
Create the directory:
bashmkdir content/packs/<new-id>Write
pack.yamlwith a uniqueid:yamlid: my-pack name: My Pack description: What this pack covers tags: [tag1, tag2] profiles: [cap-developer] # or whichever profile(s) should include itAdd content files as needed (
context.md,tips.md,tools.yaml,resources.yaml). All are optional.Reference the pack in at least one profile:
yaml# content/profiles/cap-developer.yaml packs: - id: my-pack weight: 50Test with dev mode:
bashSAP_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.
Updating Existing Content
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.
Translation Guide
How Language Resolution Works
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.
Translating Pack Content Files
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 translationThe loader automatically picks the locale-specific file when the active language matches. Falls back to the base file if the locale file is absent.
Translating Pack Metadata
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 BTPTranslating CLI Strings
CLI 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
Adding a New Language End-to-End
Create the CLI catalog:
bashcp internal/i18n/catalogs/en.json internal/i18n/catalogs/<lang>.jsonTranslate values. Leave any untranslated keys as-is (they fall back to English).
Add localised content files to each pack you want to translate:
bash# 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 translationsAdd
localesblocks to eachpack.yamlyou translated.Test:
bashsap-devs config set language <lang> SAP_DEVS_DEV=1 go run . inject --dry-run SAP_DEVS_DEV=1 go run . tipVerify translated strings appear in output.
Reset language when done testing:
bashsap-devs config set language en